Tibbles
Task 1:Loading the tidyverse package.
library(tidyverse)
Task 2:Converting the iris dataset to a tibble.
as_tibble(iris)
Task 3: Creating a tibble with columns “x,” “y,” and “z,” where “x”
ranges from 1 to 5, “y” is 1 for all rows, and “z” is calculated as the
square of “x” plus “y” for each row.
tibble(
x = 1:5,
y = 1,
z = x ^ 2 + y
)
Task 4:Creating a tibble with columns named “:)” (representing
“smile”), ” ” (representing “space”), and “2000” (representing
“number”).
tb <- tibble(
`:)` = "smile",
` ` = "space",
`2000` = "number"
)
tb
Task 5:Creating a tibble with columns “x,” “y,” and “z,” containing
the values “a,” 2, 3.6 and “b,” 1, 8.5 respectively.
tribble(
~x, ~y, ~z,
"a", 2, 3.6,
"b", 1, 8.5
)
Tibbles vs. data.frame
Task-1:Creating a tibble with columns “a,” “b,” “c,” “d,” and “e,”
containing 1000 randomly generated values for each column, representing
dates, numbers, and letters.
tibble(
a = lubridate::now() + runif(1e3) * 86400,
b = lubridate::today() + runif(1e3) * 30,
c = 1:1e3,
d = runif(1e3),
e = sample(letters, 1e3, replace = TRUE)
)
Task 2: Tnstalling the package
package_to_install <- c("nycflights13")
for (package_name in package_to_install) {
if (!requireNamespace(package_name, quietly = TRUE)) {
install.packages(package_name)
}
}
library(nycflights13)
Task 3: Printing the first 10 rows of the nycflights13::flights
dataset with unlimited width.
nycflights13::flights %>%
print(n = 10, width = Inf)
Task 4: Viewing the nycflights13::flights dataset in a separate
window for interactive exploration.
nycflights13::flights %>%
View()
Subsetting
Task 1: Creating a tibble named “df” with columns “x” and “y,” then
accessing the “x” column using different methods:
df <- tibble(
x = runif(5),#function that generates random numbers from a uniform distribution
y = rnorm(5) # function that generates random numbers from a normal (Gaussian) distribution
)
df$x
[1] 0.4134781 0.3841133 0.5761670 0.6047906 0.8490257
df[["x"]]
[1] 0.4134781 0.3841133 0.5761670 0.6047906 0.8490257
df[[1]]
[1] 0.4134781 0.3841133 0.5761670 0.6047906 0.8490257
df %>% .$x
[1] 0.4134781 0.3841133 0.5761670 0.6047906 0.8490257
Interacting with older code
Task-1: Determining the class of the object “tb” after converting it
to a data frame.
class(as.data.frame(tb))
[1] "data.frame"
Exercises
Task-1: How can you tell if an object is a tibble? (Hint: try
printing mtcars, which is a regular data frame).
mtcars
Task-2
# In a data.frame, extracting a non-existent column returns NULL,
# whereas in a tibble, it raises an error, providing immediate feedback.
# Other operations, such as extracting existing columns and subsets of columns,
# behave similarly across both data frames and tibbles.
# The default behavior of data.frames may lead to frustration
# due to the lack of error feedback for non-existent columns,
# potentially causing unnoticed mistakes and difficulty in debugging.
# In contrast, tibbles offer more robust behavior, enhancing data integrity
# and debugging efficiency.
df <- data.frame(abc = 1, xyz = "a")
# Extracting non-existent column in a data.frame
df$x # Returns NULL
[1] "a"
# Extracting existing column in a data.frame
df[, "xyz"] # Returns a data frame with one column containing the values of the "xyz" column
[1] "a"
# Extracting multiple columns in a data.frame
df[, c("abc", "xyz")] # Returns a data frame containing only the specified columns
NA
Task-3:If you have the name of a variable stored in an object,
e.g. var <- “mpg”, how can you extract the reference variable from a
tibble?
No pacakages
# heights <- read_csv("data/heights.csv")
Task 1: listing several tables: table1, table2, table3, table4a, and
table4b.
table1
table2
table3
table4a
table4b
Task 2: Calculating the rate by dividing the number of cases by the
population and then multiplying by 10,000 for table1.
table1 %>%
mutate(rate = cases / population * 10000)
Task 3: Counting the occurrences of each year in table1, using the
‘cases’ column as the weight.
table1 %>%
count(year, wt = cases)
Task 4: Creating a ggplot using table1, plotting ‘year’ against
‘cases’ with lines grouped by ‘country’ and colored in grey50, along
with points colored by ‘country’.
library(ggplot2)
ggplot(table1, aes(year, cases)) +
geom_line(aes(group = country), colour = "grey50") +
geom_point(aes(colour = country))

Pivoting
Longer
Task-1: referring to ‘table4a’
table4a
Task-2: Reshaping table4a using pivot_longer for columns ‘1999’ and
‘2000’ into ‘year’ and ‘cases’.
table4a %>%
pivot_longer(c(`1999`, `2000`), names_to = "year", values_to = "cases")
Task-3: Reshaping table4b with pivot_longer for columns ‘1999’ and
‘2000’ into ‘year’ and ‘population’.
table4b %>%
pivot_longer(c(`1999`, `2000`), names_to = "year", values_to = "population") #function transforms wide data into long format by stacking multiple columns into two: one for variable names and one for their corresponding values
Task-4: creating tidy datasets tidy4a and tidy4b by using
pivot_longer on table4a and table4b to reshape them. Then, performing a
left join on tidy4a and tidy4b.
tidy4a <- table4a %>%
pivot_longer(c(`1999`, `2000`), names_to = "year", values_to = "cases")
tidy4b <- table4b %>%
pivot_longer(c(`1999`, `2000`), names_to = "year", values_to = "population")
left_join(tidy4a, tidy4b)
Joining with `by = join_by(country, year)`
Wider
Task-1:Displaying table 2
table2
Task-2: using the pivot_wider function on table2 to transform it from
long to wide format, with ‘type’ becoming the new column names and
‘count’ being the corresponding values.
table2 %>%
pivot_wider(names_from = type, values_from = count)
Separating and uniting
Separate
Task-1:displaying table3
table3
Task-2: Using the separate function on table3 splits the ‘rate’
column into two separate columns named ‘cases’ and ‘population’.
table3 %>%
separate(rate, into = c("cases", "population"))
Task-3:Using the separate function on table3 splits the ‘rate’ column
into two separate columns named ‘cases’ and ‘population’, using the ‘/’
character as the separator.
table3 %>%
separate(rate, into = c("cases", "population"), sep = "/")
Task-4:Using the separate function on table3 splits the ‘rate’ column
into two separate columns named ‘cases’ and ‘population’, converting the
resulting columns to their appropriate data types.
table3 %>%
separate(rate, into = c("cases", "population"), convert = TRUE)
Task-5: Applying the separate function to table3, the ‘year’ column
is divided into two separate columns labeled ‘century’ and ‘year’, with
the separator defined as the second character.
table3 %>%
separate(year, into = c("century", "year"), sep = 2)
Unite
Task-1: The unite function is applied to table5 to merge the
‘century’ and ‘year’ columns into a single column named ‘new’.
table5 %>%
unite(new, century, year)
Task-2: unite function is applied to table5 to merge the ‘century’
and ‘year’ columns into a single column named ‘new’, with no separator
between them.
table5 %>%
unite(new, century, year, sep = "")
Missing values
Task-1: Create a tibble named “stocks” with columns “year”, “qtr”
(quarter), and “return”, having data for 2015 and 2016, with quarterly
returns specified and some missing entries as NA.
stocks <- tibble(
year = c(2015, 2015, 2015, 2015, 2016, 2016, 2016),
qtr = c( 1, 2, 3, 4, 2, 3, 4),
return = c(1.88, 0.59, 0.35, NA, 0.92, 0.17, 2.66)
)
Task-2:Pivoting the “stocks” tibble to widen the data, extracting
columns from the “year” variable and values from the “return”
variable.
stocks %>%
pivot_wider(names_from = year, values_from = return)
Task-3: pivot the data to a wide format with columns for each year’s
returns, then reshape it back to a long format, keeping only the
non-missing values in the “return” column.
stocks %>%
pivot_wider(names_from = year, values_from = return) %>%
pivot_longer(
cols = c(`2015`, `2016`),
names_to = "year",
values_to = "return",
values_drop_na = TRUE
)
Task-4:Filling missing combinations of “year” and “qtr” in the
“stocks” dataset.
stocks %>%
complete(year, qtr)
Task-5:Creating a tibble named “treatment” containing information
about individuals, their treatment groups, and their responses, with
some missing values for the “person” column.
treatment <- tribble(
~ person, ~ treatment, ~response,
"Derrick Whitmore", 1, 7,
NA, 2, 10,
NA, 3, 9,
"Katherine Burke", 1, 4
)
Task-6: Filling the missing values in the “person” column of the
“treatment” tibble.
treatment %>%
fill(person)
NA
Case Study
Task-1: Loading data set
who
Task-2:Pivoting the “who” dataset from wide to long format,
condensing columns into “cases” and capturing the original column names
in “key”.
who1 <- who %>%
pivot_longer(
cols = new_sp_m014:newrel_f65,
names_to = "key",
values_to = "cases",
values_drop_na = TRUE
)
who1
Task-3:Counting the occurrences of each “key” in the “who1”
dataset.
who1 %>%
count(key)
Task-4:Replacing “newrel” with “new_rel” in the “key” column of the
“who1” dataset to create “who2.”
who2 <- who1 %>%
mutate(key = stringr::str_replace(key, "newrel", "new_rel"))
who2
Task-5:Separating the “key” column in the “who2” dataset into “new,”
“type,” and “sexage” columns using “_” as the separator to create
“who3.”
who3 <- who2 %>%
separate(key, c("new", "type", "sexage"), sep = "_")
who3
Task-6:Counting the occurrences of each unique value in the “new”
column of the “who3” dataset.
who3 %>%
count(new)
Task-7:Removing the “new”, “iso2”, and “iso3” columns from the “who3”
dataset and assigning the result to “who4”.
who4 <- who3 %>%
select(-new, -iso2, -iso3)
Task-8:Splitting the “sexage” column of the “who4” dataset into “sex”
and “age” columns, separated by the first character, and assigning the
result to “who5”.
who5 <- who4 %>%
separate(sexage, c("sex", "age"), sep = 1)
who5
Task-9:Transforming the “who” dataset from wide to long format,
adjusting column names, extracting meaningful variables, dropping
unnecessary columns, and splitting the “sexage” column into “sex” and
“age”.
who %>%
pivot_longer(
cols = new_sp_m014:newrel_f65,
names_to = "key",
values_to = "cases",
values_drop_na = TRUE
) %>%
mutate(
key = stringr::str_replace(key, "newrel", "new_rel")
) %>%
separate(key, c("new", "var", "sexage")) %>%
select(-new, -iso2, -iso3) %>%
separate(sexage, c("sex", "age"), sep = 1)
NA
CH-13: Relational data
Task-1:Loding the libraries
library(tidyverse)
library(nycflights13)
nycflights13
Task-1: airlines data
airlines
Task-2: airports data
airports
Task-3: planes data
planes
Task-4: weather data
weather
Keys
Task-1Counting the occurrences of each tail number in the “planes”
table and filtering for those with more than one occurrence.
planes %>%
count(tailnum) %>%
filter(n > 1)
Task-2:Counting the occurrences of each combination of year, month,
day, hour, and origin in the “weather” table and filtering for those
with more than one occurrence.
weather %>%
count(year, month, day, hour, origin) %>%
filter(n > 1)
Task-3:Counting the occurrences of each combination of year, month,
day, and flight in the “flights” table and filtering for those with more
than one occurrence.
flights %>%
count(year, month, day, flight) %>%
filter(n > 1)
Task-4:Counting the occurrences of each combination of year, month,
day, and tail number in the “flights” table and filtering for those with
more than one occurrence.
flights %>%
count(year, month, day, tailnum) %>%
filter(n > 1)
Mutating joins
Task-1: Creating a subset of the “flights” table named “flights2”
containing columns from “year” to “day”, “hour”, “origin”, “dest”,
“tailnum”, and “carrier”.
flights2 <- flights %>%
select(year:day, hour, origin, dest, tailnum, carrier)
flights2
Task-2:Removing the “origin” and “dest” columns from “flights2” table
and then performing a left join with the “airlines” table, using the
“carrier” column as the key for matching.
flights2 %>%
select(-origin, -dest) %>%
left_join(airlines, by = "carrier")
Task-3:Shortening the command by removing “selecting” and directly
“mutating” the “name” column with the corresponding airline names from
the “airlines” table based on the “carrier” column.
flights2 %>%
select(-origin, -dest) %>%
mutate(name = airlines$name[match(carrier, airlines$carrier)])
Understanding joins
Task-1:Creating two tibbles, “x” and “y”, each with a “key” column
and an associated “val_x” or “val_y” column, respectively.
x <- tribble(
~key, ~val_x,
1, "x1",
2, "x2",
3, "x3"
)
y <- tribble(
~key, ~val_y,
1, "y1",
2, "y2",
4, "y3"
)
x
y
Inner join
Task-1:Joining tibbles x and y using an
inner join operation based on the “key” column.
x %>%
inner_join(y, by = "key")
Duplicate keys
Task-1: Joining tibble x with tibble y using the common column
“key”.
x <- tribble(
~key, ~val_x,
1, "x1",
2, "x2",
2, "x3",
1, "x4"
)
y <- tribble(
~key, ~val_y,
1, "y1",
2, "y2"
)
Task-2:Performing a left join between tibble x and
tibble y based on the common column “key”.
left_join(x, y, by = "key")
Task-3:Creating two tibbles, x and y, with
columns “key”, “val_x”, and “val_y”, populated with corresponding
values.
x <- tribble(
~key, ~val_x,
1, "x1",
2, "x2",
2, "x3",
3, "x4"
)
y <- tribble(
~key, ~val_y,
1, "y1",
2, "y2",
2, "y3",
3, "y4"
)
Task-4:Performing a left join on tibbles x and
y using the “key” column as the join key.
left_join(x, y, by = "key")
Warning: Detected an unexpected many-to-many relationship between `x` and `y`.
Defining the key columns
Task-1:Performing a left join between the flights2
tibble and the weather tibble.
flights2 %>%
left_join(weather)
Joining with `by = join_by(year, month, day, hour, origin)`
Task-2:Performing a left join between the flights2
tibble and the planes tibble using the “tailnum” column as
the key.
flights2 %>%
left_join(planes, by = "tailnum")
Task-3:Performing a left join between the flights2
tibble and the airports tibble, matching the “dest” column
from flights2 with the “faa” column from
airports.
flights2 %>%
left_join(airports, c("dest" = "faa"))
Task-4:Performing a left join between the flights2
tibble and the airports tibble, matching the “origin”
column from flights2 with the “faa” column from
airports.
flights2 %>%
left_join(airports, c("origin" = "faa"))
Filtering joins
Task-1: Calculating the top 10 destinations by counting the
occurrences in the “dest” column of the flights tibble,
sorted in descending order, and then displaying the result.
top_dest <- flights %>%
count(dest, sort = TRUE) %>%
head(10)
top_dest
Task-2: Filtering the flights tibble to include only
rows where the destination (dest) matches any of the top 10
destinations identified in the previous step.
flights %>%
filter(dest %in% top_dest$dest)
#%in% operator in R is used to check if elements in one vector are present in another vector
Task-3: Selecting rows from the flights dataset where
the destination airport matches one of the top 10 destinations
previously identified.
flights %>%
semi_join(top_dest)
Joining with `by = join_by(dest)`
Task-4: Filtering out flights with tail numbers present in the planes
dataset and counting the occurrences of each unique tail number, sorting
the result.
flights %>%
anti_join(planes, by = "tailnum") %>%
count(tailnum, sort = TRUE)
Set operations
Task-1:creating two tibbles, df1 and df2, each with columns x and y,
containing sample data.
df1 <- tribble(
~x, ~y,
1, 1,
2, 1
)
df2 <- tribble(
~x, ~y,
1, 1,
1, 2
)
Task-2:performing set operations on the tibbles df1 and df2,
including intersection, union, and set differences.
intersect(df1, df2)
union(df1, df2)
setdiff(df1, df2)
setdiff(df2, df1)
CH-14: Strings
Basic Info:string1 <- “This
is a string” string2 <- ‘If I want to include a “quote” inside a
string, I use single quotes’
Task-1:To include a literal single or double quote in a string you
can use to “escape” it
double_quote <- "\"" # or '"'
single_quote <- '\'' # or "'"
Task-2: Understanding the character
x <- c("\"", "\\") #backslash is escape character
x
[1] "\"" "\\"
writeLines(x)
"
\
String length
Task-1:
str_length(c("a", "R for data science", NA))
[1] 1 18 NA
Combining strings
Task-1:Combining the strings
str_c("x", "y")
[1] "xy"
str_c("x", "y", "z")
[1] "xyz"
Task-2:Using the sep argument to control how they’re separated.
str_c("x", "y", sep = ", ")
[1] "x, y"
Task-3:Performing concatenation with “|” and “-” at both ends of each
element of vector x, and replacing NA values with empty strings before
concatenation.
x <- c("abc", NA)
str_c("|-", x, "-|")
[1] "|-abc-|" NA
str_c("|-", str_replace_na(x), "-|")
[1] "|-abc-|" "|-NA-|"
Task-4: concatenating each element of the vector c(“a”, “b”, “c”)
with a prefix “prefix-” and a suffix “-suffix”.
str_c("prefix-", c("a", "b", "c"), "-suffix")
[1] "prefix-a-suffix" "prefix-b-suffix" "prefix-c-suffix"
Task-5: combining strings
name <- "Hadley"
time_of_day <- "morning"
birthday <- FALSE
str_c(
"Good ", time_of_day, " ", name,
if (birthday) " and HAPPY BIRTHDAY",
"."
)
[1] "Good morning Hadley."
Subsetting strings
Task-1:Extracting the first three characters from each element in the
vector x using str_sub.
x <- c("Apple", "Banana", "Pear")
str_sub(x, 1, 3)
[1] "App" "Ban" "Pea"
Task-2:negative numbers count backwards from end
str_sub(x, -3, -1)
[1] "ple" "ana" "ear"
Task-3:using the assignment form of str_sub() to modify strings
str_sub(x, 1, 1) <- str_to_lower(str_sub(x, 1, 1))
x
[1] "apple" "banana" "pear"
Locales
Task-1:Changing the case
str_to_upper(c("i", "ı"))
[1] "I" "I"
str_to_upper(c("i", "ı"), locale = "tr")
[1] "İ" "I"
Task-2:Sorting the character vector x alphabetically using the
English (en) locale and the Hawaiian (haw) locale.
x <- c("apple", "eggplant", "banana")
str_sort(x, locale = "en")
[1] "apple" "banana" "eggplant"
str_sort(x, locale = "haw")
[1] "apple" "eggplant" "banana"
Matching patterns with regular expressions
Basic matches
Task-1:Searching for the pattern “an” within each element of
x and displaying the matches.
x <- c("apple", "banana", "pear")
str_view(x, "an")
[2] │ b<an><an>a
Task-2:Displaying elements in x where any character is
followed by “a” and then any character.
str_view(x, ".a.")
[2] │ <ban>ana
[3] │ p<ear>
Task-3
# To create the regular expression, we need \\
dot <- "\\."
# But the expression itself only contains one:
writeLines(dot)
\.
# And this tells R to look for an explicit .
str_view(c("abc", "a.c", "bef"), "a\\.c")
[2] │ <a.c>
Task-4: Displaying elements in x where the sequence “\”
occurs.
x <- "a\\b"
writeLines(x)
a\b
str_view(x, "\\\\")
[1] │ a<\>b
Anchors
Task-1: Displaying elements in x that start with “a” and
end with “a” respectively.
x <- c("apple", "banana", "pear")
str_view(x, "^a")
[1] │ <a>pple
str_view(x, "a$")
[2] │ banan<a>
Task-2: Highlighting “apple” occurrences in x and
instances where it’s the only content.
x <- c("apple pie", "apple", "apple cake")
str_view(x, "apple")
[1] │ <apple> pie
[2] │ <apple>
[3] │ <apple> cake
str_view(x, "^apple$")
[2] │ <apple>
Character classes and alternatives
Task-1: Visualizing patterns matching “a.c”, “a*c”, and “a c” in the
provided character vector.
str_view(c("abc", "a.c", "a*c", "a c"), "a[.]c")
[2] │ <a.c>
str_view(c("abc", "a.c", "a*c", "a c"), ".[*]c")
[3] │ <a*c>
str_view(c("abc", "a.c", "a*c", "a c"), "a[ ]")
[4] │ <a >c
Task-2: Visualizing patterns matching “grey” or “gray” in the
provided character vector.
str_view(c("grey", "gray"), "gr(e|a)y")
[1] │ <grey>
[2] │ <gray>
Repetition
Task-1:Identifying patterns “CC” or “C” in the string “1888 is the
longest year in Roman numerals
x <- "1888 is the longest year in Roman numerals: MDCCCLXXXVIII"
str_view(x, "CC?")
[1] │ 1888 is the longest year in Roman numerals: MD<CC><C>LXXXVIII
Task-2: Viewing the pattern “CC”
str_view(x, "CC+")
[1] │ 1888 is the longest year in Roman numerals: MD<CCC>LXXXVIII
Task-3: Viewing the pattern “C[LX]+”
str_view(x, 'C[LX]+')
[1] │ 1888 is the longest year in Roman numerals: MDCC<CLXXX>VIII
Task-4:Viewing the pattern “C{2},C{2,},c{2,3}”
str_view(x, "C{2}")
[1] │ 1888 is the longest year in Roman numerals: MD<CC>CLXXXVIII
str_view(x, "C{2,}")
[1] │ 1888 is the longest year in Roman numerals: MD<CCC>LXXXVIII
str_view(x, "C{2,3}")
[1] │ 1888 is the longest year in Roman numerals: MD<CCC>LXXXVIII
Grouping and backreferences
Task-1:Grouping
str_view(fruit, "(..)\\1", match = TRUE)
[4] │ b<anan>a
[20] │ <coco>nut
[22] │ <cucu>mber
[41] │ <juju>be
[56] │ <papa>ya
[73] │ s<alal> berry
Detect matches
Task-1: Checking for the presence of the letter “e” in each word
x <- c("apple", "banana", "pear")
str_detect(x, "e")
[1] TRUE FALSE TRUE
Task-2:Checking how many common words start with t
sum(str_detect(words, "^t"))
[1] 65
Task-3: Checking proportion of common words end with a vowel
mean(str_detect(words, "[aeiou]$"))
[1] 0.2765306
Task-4:Finding all words containing at least one vowel, and
negate
no_vowels_1 <- !str_detect(words, "[aeiou]")
Task-5:Finding all words consisting only of consonants
(non-vowels)
no_vowels_2 <- str_detect(words, "^[^aeiou]+$")
identical(no_vowels_1, no_vowels_2)
[1] TRUE
Task-6: Filtering words that end with the letter “x” from a list of
words.
words[str_detect(words, "x$")]
[1] "box" "sex" "six" "tax"
str_subset(words, "x$")
[1] "box" "sex" "six" "tax"
Task-7: Filtering a tibble for words that end with “x”.
df <- tibble(
word = words,
i = seq_along(word)
)
df %>%
filter(str_detect(word, "x$"))
Task-8:Counting the occurrences of “a” in each element of a character
vector.
x <- c("apple", "banana", "pear")
str_count(x, "a")
[1] 1 3 1
Task-9: Seeing average of how many vowels per word
mean(str_count(words, "[aeiou]"))
[1] 1.991837
Task-10: Adding columns to a tibble to count vowels and consonants in
each word.
df %>%
mutate(
vowels = str_count(word, "[aeiou]"),
consonants = str_count(word, "[^aeiou]")
)
Task-11:Counting “aba” occurrences in “abababa” and showing all “aba”
instances.
str_count("abababa", "aba")
[1] 2
str_view_all("abababa", "aba")
Warning: `str_view_all()` was deprecated in stringr 1.5.0.
Please use `str_view()` instead.
[1] │ <aba>b<aba>
Grouped matches
Task-1: Extracting sentences containing nouns defined by a pattern,
then extracts the nouns from those sentences.
noun <- "(a|the) ([^ ]+)"
has_noun <- sentences %>%
str_subset(noun) %>%
head(10)
has_noun %>%
str_extract(noun)
[1] "the smooth" "the sheet" "the depth" "a chicken" "the parked" "the sun" "the huge" "the ball"
[9] "the woman" "a helps"
Task-2:
has_noun %>%
str_match(noun)
[,1] [,2] [,3]
[1,] "the smooth" "the" "smooth"
[2,] "the sheet" "the" "sheet"
[3,] "the depth" "the" "depth"
[4,] "a chicken" "a" "chicken"
[5,] "the parked" "the" "parked"
[6,] "the sun" "the" "sun"
[7,] "the huge" "the" "huge"
[8,] "the ball" "the" "ball"
[9,] "the woman" "the" "woman"
[10,] "a helps" "a" "helps"
Task-3:Creating a tibble with columns ‘article’ and ‘noun’ extracted
from sentences based on a pattern.
tibble(sentence = sentences) %>%
tidyr::extract(
sentence, c("article", "noun"), "(a|the) ([^ ]+)",
remove = FALSE
)
Replacing matches
Task-1: Replacing the first vowel in each word of x with a hyphen.
Replacing all vowels in each word of x with a hyphen.
x <- c("apple", "pear", "banana")
str_replace(x, "[aeiou]", "-")
[1] "-pple" "p-ar" "b-nana"
str_replace_all(x, "[aeiou]", "-")
[1] "-ppl-" "p--r" "b-n-n-"
Task-2: Replacing numeric values in x with their corresponding word
representations.
x <- c("1 house", "2 cars", "3 people")
str_replace_all(x, c("1" = "one", "2" = "two", "3" = "three"))
[1] "one house" "two cars" "three people"
Task-3:Reordering words in sentences by swapping the second and third
word positions.
sentences %>%
str_replace("([^ ]+) ([^ ]+) ([^ ]+)", "\\1 \\3 \\2") %>%
head(5)
[1] "The canoe birch slid on the smooth planks." "Glue sheet the to the dark blue background."
[3] "It's to easy tell the depth of a well." "These a days chicken leg is a rare dish."
[5] "Rice often is served in round bowls."
Splitting
Task-1: Splitting the first five sentences into words.
sentences %>%
head(5) %>%
str_split(" ")
[[1]]
[1] "The" "birch" "canoe" "slid" "on" "the" "smooth" "planks."
[[2]]
[1] "Glue" "the" "sheet" "to" "the" "dark" "blue"
[8] "background."
[[3]]
[1] "It's" "easy" "to" "tell" "the" "depth" "of" "a" "well."
[[4]]
[1] "These" "days" "a" "chicken" "leg" "is" "a" "rare" "dish."
[[5]]
[1] "Rice" "is" "often" "served" "in" "round" "bowls."
Task-2:Splitting the string ‘a|b|c|d’ by ‘|’ into a vector of
elements.
"a|b|c|d" %>%
str_split("\\|") %>%
.[[1]]
[1] "a" "b" "c" "d"
Task-3:Splitting the first 5 sentences by space into a matrix of
words.
sentences %>%
head(5) %>%
str_split(" ", simplify = TRUE)
[,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9]
[1,] "The" "birch" "canoe" "slid" "on" "the" "smooth" "planks." ""
[2,] "Glue" "the" "sheet" "to" "the" "dark" "blue" "background." ""
[3,] "It's" "easy" "to" "tell" "the" "depth" "of" "a" "well."
[4,] "These" "days" "a" "chicken" "leg" "is" "a" "rare" "dish."
[5,] "Rice" "is" "often" "served" "in" "round" "bowls." "" ""
Task-4:Splitting each field string into two parts at the first
occurrence of ‘:’.
fields <- c("Name: Hadley", "Country: NZ", "Age: 35")
fields %>% str_split(": ", n = 2, simplify = TRUE)
[,1] [,2]
[1,] "Name" "Hadley"
[2,] "Country" "NZ"
[3,] "Age" "35"
Task-5: Display word boundaries, split by spaces, and split by word
boundaries, respectively.
x <- "This is a sentence. This is another sentence."
str_view_all(x, boundary("word"))
[1] │ <This> <is> <a> <sentence>. <This> <is> <another> <sentence>.
str_split(x, " ")[[1]]
[1] "This" "is" "a" "sentence." "" "This" "is" "another"
[9] "sentence."
str_split(x, boundary("word"))[[1]]
[1] "This" "is" "a" "sentence" "This" "is" "another" "sentence"
Other types of pattern
Task-1:
# The regular call:
str_view(fruit, "nana")
[4] │ ba<nana>
# Is shorthand for
str_view(fruit, regex("nana"))
[4] │ ba<nana>
Task-2:Visualizing occurrences of “banana” in different case
variations.
bananas <- c("banana", "Banana", "BANANA")
str_view(bananas, "banana")
[1] │ <banana>
str_view(bananas, regex("banana", ignore_case = TRUE))
[1] │ <banana>
[2] │ <Banana>
[3] │ <BANANA>
Task-3: Extracting all lines starting with “Line” from the text.
x <- "Line 1\nLine 2\nLine 3"
str_extract_all(x, "^Line")[[1]]
[1] "Line"
Task-4: Extracting all occurrences of lines starting with “Line” from
the text, considering each line separately.
str_extract_all(x, regex("^Line", multiline = TRUE))[[1]]
[1] "Line" "Line" "Line"
Task-5:Creating a regular expression pattern for phone numbers,
allowing for variations in formatting, and attempting to match it
against the provided phone number.
phone <- regex("
\\(? # optional opening parens
(\\d{3}) # area code
[) -]? # optional closing parens, space, or dash
(\\d{3}) # another three numbers
[ -]? # optional space or dash
(\\d{3}) # three more numbers
", comments = TRUE)
str_match("514-791-8141", phone)
[,1] [,2] [,3] [,4]
[1,] "514-791-814" "514" "791" "814"
Task-6:Installling the package and Benchmarking string detection in
“sentences” using fixed and regex patterns 20 times each, comparing
performance with microbenchmark.
package_to_install <- c("microbenchmark")
for (package_name in package_to_install) {
if (!requireNamespace(package_name, quietly = TRUE)) {
install.packages(package_name)
}
}
library(microbenchmark)
microbenchmark::microbenchmark(
fixed = str_detect(sentences, fixed("the")),
regex = str_detect(sentences, "the"),
times = 20
)
Unit: microseconds
Task-7:Starting with a1 being “0e1” and a2 being “a301”, both
representing the character “á”, they are compared for equality.
a1 <- "\u00e1"
a2 <- "a\u0301"
c(a1, a2)
[1] "á" "á"
a1 == a2
[1] FALSE
Task-8: Checking if a1 contains the fixed string
a2 returns FALSE, whereas using collation
rules returns TRUE.
str_detect(a1, fixed(a2))
[1] FALSE
str_detect(a1, coll(a2))
[1] TRUE
Task-9:Creating a vector i with different forms of the
letter “i”, then using str_subset to filter them based on
collation.
i <- c("I", "İ", "i", "ı")
i
[1] "I" "İ" "i" "ı"
str_subset(i, coll("i", ignore_case = TRUE))
[1] "I" "i"
str_subset(i, coll("i", ignore_case = TRUE, locale = "tr"))
[1] "İ" "i"
Task-10: Fetching locale information.
stringi::stri_locale_info()
$Language
[1] "en"
$Country
[1] "US"
$Variant
[1] ""
$Name
[1] "en_US"
Task-11:Visualizing word boundaries and extracts all words from the
string.
x <- "This is a sentence."
str_view_all(x, boundary("word"))
[1] │ <This> <is> <a> <sentence>.
str_extract_all(x, boundary("word"))
[[1]]
[1] "This" "is" "a" "sentence"
CH-15: Factors
Creatig factors
Task-1:Adding character vector in variable x1
x1 <- c("Dec", "Apr", "Jan", "Mar")
Task-2:Adding character vector in variable x2
x2 <- c("Dec", "Apr", "Jam", "Mar")
Task-3:Sorting X1
sort(x1)
[1] "Apr" "Dec" "Jan" "Mar"
Task-4:Adding Character vector in month_levels
month_levels <- c(
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
)
Task-5:Assigning the factor levels to the variable x1, using the
predefined month_levels.
y1 <- factor(x1, levels = month_levels)
y
Task-6:Sorting the factor levels in y1.
sort(y1)
[1] Jan Mar Apr Dec
Levels: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
Task-7:creating a factor y2 from x2 with custom levels specified by
month_levels.
y2 <- factor(x2, levels = month_levels)
y2
[1] Dec Apr <NA> Mar
Levels: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
Task-8:parsing the values in x2 as factors
y2 <- parse_factor(x2, levels = month_levels)
Warning: 1 parsing failure.
row col expected actual
3 -- value in level set Jam
Task-9: omitting the levels.
factor(x1)
[1] Dec Apr Jan Mar
Levels: Apr Dec Jan Mar
Task-10:Creating a factor f1 from the values in x1, using the unique
values of x1 as levels.
f1 <- factor(x1, levels = unique(x1))
f1
[1] Dec Apr Jan Mar
Levels: Dec Apr Jan Mar
Task-11: creating a factor f2 from the values in x1, ordering them
according to their appearance in x1.
f2 <- x1 %>% factor() %>% fct_inorder()
f2
[1] Dec Apr Jan Mar
Levels: Dec Apr Jan Mar
Task-12:Omitting levels2
levels(f2)
[1] "Dec" "Apr" "Jan" "Mar"
General Social Survey
Task-1:Loading datasets
gss_cat
Task-2:Seeing levels through count()
gss_cat %>%
count(race)
Task-3:Also seeing through bar()
ggplot(gss_cat, aes(race)) +
geom_bar()

Task-4:Generating a bar plot using ggplot()
ggplot(gss_cat,aes(race))+geom_bar()+scale_x_discrete(drop=FALSE)

Modifying factor order
Task-1:calculating summary statistics and then creating scatter
plot
relig_summary <- gss_cat %>%
group_by(relig) %>%
summarise(
age = mean(age, na.rm = TRUE),
tvhours = mean(tvhours, na.rm = TRUE),
n = n()
)
ggplot(relig_summary, aes(tvhours, relig)) + geom_point()

Task-2:Generating a scatter plot using ggplot, where the
x-axis represents the mean TV hours (tvhours), and the
y-axis represents the relig variable reordered by mean TV
hours.
ggplot(relig_summary, aes(tvhours, fct_reorder(relig, tvhours))) +
geom_point()

Task-3:Creating a scatter plot using ggplot.
relig_summary %>%
mutate(relig = fct_reorder(relig, tvhours)) %>%
ggplot(aes(tvhours, relig)) +
geom_point()

Task-4:Generating a scatter plot using ggplot
rincome_summary <- gss_cat %>%
group_by(rincome) %>%
summarise(
age = mean(age, na.rm = TRUE),
tvhours = mean(tvhours, na.rm = TRUE),
n = n()
)
ggplot(rincome_summary, aes(age, fct_reorder(rincome, age))) + geom_point()

Task-5: creates a scatter plot of the average age by income level,
with “Not applicable” as the reference level for income
ggplot(rincome_summary, aes(age, fct_relevel(rincome, "Not applicable"))) +
geom_point()

Task-6:calculating the proportion of each marital status group across
different age groups and creates a line plot showing the distribution of
marital status proportions by age.
by_age <- gss_cat %>%
filter(!is.na(age)) %>%
count(age, marital) %>%
group_by(age) %>%
mutate(prop = n / sum(n))
ggplot(by_age, aes(age, prop, colour = marital)) +
geom_line(na.rm = TRUE)

ggplot(by_age, aes(age, prop, colour = fct_reorder2(marital, age, prop))) +
geom_line() +
labs(colour = "marital")

Task-7: Adjusting the order of the “marital” variable based on
frequency and then reverses the order before generating a bar plot
illustrating the distribution of marital status.
gss_cat %>%
mutate(marital = marital %>% fct_infreq() %>% fct_rev()) %>%
ggplot(aes(marital)) +
geom_bar()

Modifying factor levels
Task-1: counting the frequency of each unique value in the “partyid”
variable of the “gss_cat” dataset.
gss_cat%>%count(partyid)
Task-2:Recording the levels of the “partyid” variable in the
“gss_cat” dataset and then counts the frequency of each unique recorded
value.
gss_cat %>%
mutate( partyid=fct_recode(partyid,
"Republican, strong" = "Strong republican",
"Republican, weak" = "Not str republican",
"Independent, near rep" = "Ind,near rep",
"Independent, near dem" = "Ind,near dem",
"Democrat, weak" = "Not str democrat",
"Democrat, strong" = "Strong democrat"
))%>%
count(partyid)
Task-3:Recategorizing and counting party affiliations in the
“gss_cat” dataset.
gss_cat %>%
mutate(partyid = fct_recode(partyid,
"Republican, strong" = "Strong republican",
"Republican, weak" = "Not str republican",
"Independent, near rep" = "Ind,near rep",
"Independent, near dem" = "Ind,near dem",
"Democrat, weak" = "Not str democrat",
"Democrat, strong" = "Strong democrat",
"Other" = "No answer",
"Other" = "Don't know",
"Other" = "Other party"
)) %>%
count(partyid)
Task-4: Collapsing categories within the “partyid” variable in the
“gss_cat” dataset into broader groups and then counting the frequency of
each collapsed category.
gss_cat%>%
mutate(partyid=fct_collapse(partyid,
other=c("No answer", "Don't know", "Other party"),
rep=c("Strong republican", "Not str republican"),
ind=c("Ind,near rep", "Independent", "Ind,near dem"),
dem=c("Not str democrat", "Strong democrat"))) %>%
count(partyid)
Task-5:Counting and aggregating religious affiliations in the
“gss_cat” dataset after lumping together less frequent categories.
gss_cat %>%
mutate(relig = fct_lump(relig)) %>%
count(relig)
Task-6:“Summarizing religious affiliations after lumping infrequent
categories and sort.”
gss_cat %>%
mutate(relig = fct_lump(relig, n = 10)) %>%
count(relig, sort = TRUE) %>%
print(n = Inf)
CH-Data and Times
Task-1:Loading library
library(tidyverse)
library(lubridate)
library(nycflights13)
Creating dates/times
Task-1: Printing current date or date-time
today()
[1] "2024-05-04"
now()
[1] "2024-05-04 20:51:16 +0545"
Form strings
Task-2:converting date strings to date objects in different
formats.
ymd("2017-01-31")
[1] "2017-01-31"
mdy("January 31st, 2017")
[1] "2017-01-31"
dmy("31-Jan-2017")
[1] "2017-01-31"
ymd(20170131)
[1] "2017-01-31"
ymd_hms("2017-01-31 20:11:59")
[1] "2017-01-31 20:11:59 UTC"
mdy_hm("01/31/2017 08:01")
[1] "2017-01-31 08:01:00 UTC"
flights %>%
select(year, month, day, hour, minute)
flights %>%
select(year, month, day, hour, minute) %>%
mutate(departure = make_datetime(year, month, day, hour, minute))
Task: Creating date-time objects from hour-minute time data in the
‘flights’ dataset and filtering out rows with missing departure or
arrival times
make_datetime_100 <- function(year, month, day, time) {
make_datetime(year, month, day, time %/% 100, time %% 100)
}
flights_dt <- flights %>%
filter(!is.na(dep_time), !is.na(arr_time)) %>%
mutate(
dep_time = make_datetime_100(year, month, day, dep_time),
arr_time = make_datetime_100(year, month, day, arr_time),
sched_dep_time = make_datetime_100(year, month, day, sched_dep_time),
sched_arr_time = make_datetime_100(year, month, day, sched_arr_time)
) %>%
select(origin, dest, ends_with("delay"), ends_with("time"))
flights_dt
Task: Plotting the frequency of flights over time using departure
date-time
flights_dt %>%
ggplot(aes(dep_time)) +
geom_freqpoly(binwidth = 86400)

Task: Plotting the frequency of flights over time for a specific
period using departure date-time
flights_dt %>%
filter(dep_time < ymd(20130102)) %>%
ggplot(aes(dep_time)) +
geom_freqpoly(binwidth = 600) # 600 s = 10 minutes

Task: to convert today’s date to date-time object
as_datetime(today())
[1] "2024-05-04 UTC"
as_date(now())
[1] "2024-05-04"
as_date(365 * 10 + 2)
[1] "1980-01-01"
Date-time components Task: Extracting various components of a
date-time object
datetime <- ymd_hms("2016-07-08 12:34:56")
year(datetime)
[1] 2016
month(datetime)
[1] 7
mday(datetime)
[1] 8
yday(datetime)
[1] 190
wday(datetime)
[1] 6
month(datetime, label = TRUE)
[1] Jul
Levels: Jan < Feb < Mar < Apr < May < Jun < Jul < Aug < Sep < Oct < Nov < Dec
wday(datetime, label = TRUE, abbr = FALSE)
[1] Friday
Levels: Sunday < Monday < Tuesday < Wednesday < Thursday < Friday < Saturday
Task: Plotting the frequency of flights by day of the week
flights_dt %>%
mutate(wday = wday(dep_time, label = TRUE)) %>%
ggplot(aes(x = wday)) +
geom_bar()

Task: Plotting average delay by minute of departure time
flights_dt %>%
mutate(minute = minute(dep_time)) %>%
group_by(minute) %>%
summarise(
avg_delay = mean(arr_delay, na.rm = TRUE),
n = n()) %>%
ggplot(aes(minute, avg_delay)) +
geom_line()

Task: Plotting average delay by minute of scheduled departure
time
sched_dep <- flights_dt %>%
mutate(minute = minute(sched_dep_time)) %>%
group_by(minute) %>%
summarise(
avg_delay = mean(arr_delay, na.rm = TRUE),
n = n())
ggplot(sched_dep, aes(minute, avg_delay)) +
geom_line()

Task: Plotting the number of flights by minute of scheduled departure
time
ggplot(sched_dep, aes(minute, n)) +
geom_line()

Rounding Task:Plotting the number of flights by week, rounding to the
nearest week
flights_dt %>%
count(week = floor_date(dep_time, "week")) %>%
ggplot(aes(week, n)) +
geom_line()

setting compounds Task: Setting up a date-time object
(datetime <- ymd_hms("2016-07-08 12:34:56"))
[1] "2016-07-08 12:34:56 UTC"
year(datetime) <- 2020
datetime
[1] "2020-07-08 12:34:56 UTC"
month(datetime) <- 01
datetime
[1] "2020-01-08 12:34:56 UTC"
hour(datetime) <- hour(datetime) + 1
datetime
[1] "2020-01-08 13:34:56 UTC"
update(datetime, year = 2020, month = 2, mday = 2, hour = 2)
[1] "2020-02-02 02:34:56 UTC"
ymd("2015-02-01") %>%
update(mday = 30)
[1] "2015-03-02"
ymd("2015-02-01") %>%
update(hour = 400)
[1] "2015-02-17 16:00:00 UTC"
Task: Creating a new variable ‘dep_hour’ by updating the ‘dep_time’
to the first day of the year
flights_dt %>%
mutate(dep_hour = update(dep_time, yday = 1)) %>%
ggplot(aes(dep_hour)) +
geom_freqpoly(binwidth = 300)

Time Spans Compute the age of a person based on their birthdate and
today’s date
h_age <- today() - ymd(19791014)
h_age
Time difference of 16274 days
as.duration(h_age)
[1] "1406073600s (~44.56 years)"
dseconds(15)
[1] "15s"
dminutes(10)
[1] "600s (~10 minutes)"
dhours(c(12, 24))
[1] "43200s (~12 hours)" "86400s (~1 days)"
ddays(0:5)
[1] "0s" "86400s (~1 days)" "172800s (~2 days)" "259200s (~3 days)" "345600s (~4 days)"
[6] "432000s (~5 days)"
dweeks(3)
[1] "1814400s (~3 weeks)"
dyears(1)
[1] "31557600s (~1 years)"
2 * dyears(1)
[1] "63115200s (~2 years)"
dyears(1) + dweeks(12) + dhours(15)
[1] "38869200s (~1.23 years)"
tomorrow <- today() + ddays(1)
last_year <- today() - dyears(1)
one_pm <- ymd_hms("2016-03-12 13:00:00", tz = "America/New_York")
one_pm
[1] "2016-03-12 13:00:00 EST"
one_pm + ddays(1)
[1] "2016-03-13 14:00:00 EDT"
Periods Create period objects representing different time spans and
Perform arithmetic operations with period objects
one_pm
[1] "2016-03-12 13:00:00 EST"
one_om = days(1)
seconds(15)
[1] "15S"
minutes(10)
[1] "10M 0S"
hours(c(12, 24))
[1] "12H 0M 0S" "24H 0M 0S"
days(7)
[1] "7d 0H 0M 0S"
months(1:6)
[1] "1m 0d 0H 0M 0S" "2m 0d 0H 0M 0S" "3m 0d 0H 0M 0S" "4m 0d 0H 0M 0S" "5m 0d 0H 0M 0S" "6m 0d 0H 0M 0S"
weeks(3)
[1] "21d 0H 0M 0S"
years(1)
[1] "1y 0m 0d 0H 0M 0S"
10 * (months(6) + days(1))
[1] "60m 10d 0H 0M 0S"
days(50) + hours(25) + minutes(2)
[1] "50d 25H 2M 0S"
ymd("2016-01-01") + dyears(1)
[1] "2016-12-31 06:00:00 UTC"
ymd("2016-01-01") + years(1)
[1] "2017-01-01"
one_pm + ddays(1)
[1] "2016-03-13 14:00:00 EDT"
one_pm + days(1)
[1] "2016-03-13 13:00:00 EDT"
Filter flights where arrival time is before departure time
flights_dt %>%
filter(arr_time < dep_time)
Update flights data to correct overnight flights
flights_dt <- flights_dt %>%
mutate(
overnight = arr_time < dep_time,
arr_time = arr_time + days(overnight * 1),
sched_arr_time = sched_arr_time + days(overnight * 1)
)
Filter flights where overnight condition is true and arrival time is
before departure time
flights_dt %>%
filter(overnight, arr_time < dep_time)
Intervals Calculate the ratio of one year in days
years(1) / days(1)
[1] 365.25
next_year <- today() + years(1)
(today() %--% next_year) / ddays(1)
[1] 365
(today() %--% next_year) %/% days(1)
[1] 365
Display time zone information
Sys.timezone()
[1] "Asia/Katmandu"
length(OlsonNames())
[1] 596
head(OlsonNames())
[1] "Africa/Abidjan" "Africa/Accra" "Africa/Addis_Ababa" "Africa/Algiers" "Africa/Asmara"
[6] "Africa/Asmera"
(x1 <- ymd_hms("2015-06-01 12:00:00", tz = "America/New_York"))
[1] "2015-06-01 12:00:00 EDT"
(x2 <- ymd_hms("2015-06-01 18:00:00", tz = "Europe/Copenhagen"))
[1] "2015-06-01 18:00:00 CEST"
(x3 <- ymd_hms("2015-06-02 04:00:00", tz = "Pacific/Auckland"))
[1] "2015-06-02 04:00:00 NZST"
x1 - x2
Time difference of 0 secs
x1 - x3
Time difference of 0 secs
Pipes
Task: To import the required library
packages_to_install <- c("tidyverse", "pryr")
for (package_name in packages_to_install) {
if (!requireNamespace(package_name, quietly = TRUE)) {
install.packages(package_name)
}
library(package_name, character.only = TRUE)
}
library(magrittr)
Create diamond data and calculate the object sizes
diamonds <- ggplot2::diamonds
diamonds2 <- diamonds %>%
dplyr::mutate(price_per_carat = price / carat)
pryr::object_size(diamonds)
3.46 MB
pryr::object_size(diamonds2)
3.89 MB
pryr::object_size(diamonds, diamonds2)
3.89 MB
Functions Normalize the columns of a data frame
df <- tibble::tibble(
a = rnorm(10),
b = rnorm(10),
c = rnorm(10),
d = rnorm(10)
)
df$a <- (df$a - min(df$a, na.rm = TRUE)) /
(max(df$a, na.rm = TRUE) - min(df$a, na.rm = TRUE))
df$b <- (df$b - min(df$b, na.rm = TRUE)) /
(max(df$b, na.rm = TRUE) - min(df$a, na.rm = TRUE))
df$c <- (df$c - min(df$c, na.rm = TRUE)) /
(max(df$c, na.rm = TRUE) - min(df$c, na.rm = TRUE))
df$d <- (df$d - min(df$d, na.rm = TRUE)) /
(max(df$d, na.rm = TRUE) - min(df$d, na.rm = TRUE))
Normalize a single column of a data frame
(df$a - min(df$a, na.rm = TRUE)) /
(max(df$a, na.rm = TRUE) - min(df$a, na.rm = TRUE))
[1] 0.2660918 0.1288832 0.0769690 0.3163641 0.5612945 0.6241704 0.5271891 0.0000000 0.3913369 1.0000000
x <- df$a
(x - min(x, na.rm = TRUE)) / (max(x, na.rm = TRUE) - min(x, na.rm = TRUE))
[1] 0.2660918 0.1288832 0.0769690 0.3163641 0.5612945 0.6241704 0.5271891 0.0000000 0.3913369 1.0000000
rng <- range(x, na.rm = TRUE)
(x - rng[1]) / (rng[2] - rng[1])
[1] 0.2660918 0.1288832 0.0769690 0.3163641 0.5612945 0.6241704 0.5271891 0.0000000 0.3913369 1.0000000
rescale01 <- function(x) {
rng <- range(x, na.rm = TRUE)
(x - rng[1]) / (rng[2] - rng[1])
}
rescale01(c(0, 5, 10))
[1] 0.0 0.5 1.0
Rescale a vector to the range [0, 1]
rescale01(c(-10, 0, 10))
[1] 0.0 0.5 1.0
rescale01(c(1, 2, 3, NA, 5))
[1] 0.00 0.25 0.50 NA 1.00
Rescale each column of a DataFrame to the range [0, 1]
df$a <- rescale01(df$a)
df$b <- rescale01(df$b)
df$c <- rescale01(df$c)
df$d <- rescale01(df$d)
x <- c(1:10, Inf)
rescale01(x)
[1] 0 0 0 0 0 0 0 0 0 0 NaN
Define the rescale01 function and apply it
rescale01 <- function(x) {
rng <- range(x, na.rm = TRUE, finite = TRUE)
(x - rng[1]) / (rng[2] - rng[1])
}
rescale01(x)
[1] 0.0000000 0.1111111 0.2222222 0.3333333 0.4444444 0.5555556 0.6666667 0.7777778 0.8888889 1.0000000
[11] Inf
Load required libraries and packages
library(tidyverse)
library(purrr)
library(magrittr)
# install.packages("pryr")
library(pryr)
18.2 Piping alternatives
This is a popular Children’s poem that is accompanied by hand
actions.We’ll start by defining an object to represent little bunny Foo
Foo:
# foo_foo <- little_bunny()
18.2.2 Overwrite the original
Instead of creating intermediate objects at each step, we could
overwrite the original object:
# foo_foo <- hop(foo_foo, through = forest)
# foo_foo <- scoop(foo_foo, up = field_mice)
# foo_foo <- bop(foo_foo, on = head)
18.2.3 Function composition
Another approach is to abandon assignment and just string the
function calls together:
# bop(
# scoop(
# hop(foo_foo, through = forest),
# up = field_mice
# ),
# on = head
# )
Here the disadvantage is that you have to read from inside-out, from
right-to-left, and that the arguments end up spread far apart
(evocatively called the dagwood sandwhich problem). In short, this code
is hard for a human to consume.
18.2.4 Use the pipe
Finally, we can use the pipe:
# foo_foo %>%
# hop(through = forest) %>%
# scoop(up = field_mice) %>%
# bop(on = head)
# my_pipe <- function(.) {
# . <- hop(., through = forest)
# . <- scoop(., up = field_mice)
# bop(., on = head)
# }
# my_pipe(foo_foo)
TASK: Functions that use the current environment. For example,
assign() will create a new variable with the given name in
the current environment:
assign("x",10)
x
[1] 10
"x" %>% assign(100)
x
[1] 10
Assign value to “x” in the specified environment and check its value
and Generate random numbers, create a matrix, plot it, and inspect its
structure
env <- environment()
"x" %>% assign(100,envir=env)
x
[1] 100
rnorm(100) %>%
matrix(ncol=2) %>%
plot() %>%
str()
NULL

rnorm(100) %>%
matrix(ncol=2) %>%
plot() %>%
str()
NULL

ndist <- rnorm(100000)
hist(ndist)

Calculate the correlation between two variables in mtcars dataset
mtcars %$%
cor(disp, mpg)
[1] -0.8475514
- For assignment magrittr provides the
%<>%
operator which allows you to replace code like:
mtcars <- mtcars %>%
transform(cyl=cyl*2)
mtcars %<>% transform(cyl=cyl*2)
Chapter 19 Functions
19.1 Introduction
19.2 When should you write a function?
df <- tibble::tibble(
a = rnorm(10),
b = rnorm(10),
c = rnorm(10),
d = rnorm(10)
)
df
df$a <- (df$a - min(df$a, na.rm = TRUE)) /
(max(df$a, na.rm = TRUE) - min(df$a, na.rm = TRUE))
df$b <- (df$b - min(df$b, na.rm = TRUE)) /
(max(df$b, na.rm = TRUE) - min(df$a, na.rm = TRUE))
df$c <- (df$c - min(df$c, na.rm = TRUE)) /
(max(df$c, na.rm = TRUE) - min(df$c, na.rm = TRUE))
df$d <- (df$d - min(df$d, na.rm = TRUE)) /
(max(df$d, na.rm = TRUE) - min(df$d, na.rm = TRUE))
Rescale a single variable in a data frame
(df$a - min(df$a, na.rm = TRUE)) /
(max(df$a, na.rm = TRUE) - min(df$a, na.rm = TRUE))
[1] 0.5235603 0.5627738 0.2777349 0.0000000 1.0000000 0.1019340 0.3832672 0.9030480 0.6526363 0.6825633
Rescale a single variable without creating a new object
x <- df$a
(x - min(x, na.rm = T)) / (max(x, na.rm = T)-min(x, na.rm = T))
[1] 0.5235603 0.5627738 0.2777349 0.0000000 1.0000000 0.1019340 0.3832672 0.9030480 0.6526363 0.6825633
Task: There is some duplication in this code. We’re computing the
range of the data three times, so it makes sense to do it in one
step:
rng <- range(x, na.rm = T)
(x-rng[1])/(rng[2]-rng[1])
[1] 0.5235603 0.5627738 0.2777349 0.0000000 1.0000000 0.1019340 0.3832672 0.9030480 0.6526363 0.6825633
Pulling out intermediate calculations into named variables is a good
practice because it makes it more clear what the code is doing. Now that
I’ve simplified the code, and checked that it still works, I can turn it
into a function:
rescale01 <- function(x){
rng <- range(x, na.rm = T)
(x-rng[1])/(rng[2]-rng[1])
}
rescale01(c(0,5,10))
[1] 0.0 0.5 1.0
Test the rescale01 function with various inputs
rescale01(c(-10,0,10))
[1] 0.0 0.5 1.0
rescale01(c(1,2,3,NA,5))
[1] 0.00 0.25 0.50 NA 1.00
We can simplify the original example now that we have a function:
df$a <- rescale01(df$a)
df$b <- rescale01(df$b)
df$c <- rescale01(df$c)
df$d <- rescale01(df$d)
Rescale a vector with infinite values
x <- c(1:10,Inf)
rescale01(x)
[1] 0 0 0 0 0 0 0 0 0 0 NaN
Because we’ve extracted the code into a function, we only need to
make the fix in one place:
rescale01 <- function(x){
rng <- range(x,na.rm=T,finite=T)
(x-rng[1])/(rng[2]-rng[1])
}
rescale01(x)
[1] 0.0000000 0.1111111 0.2222222 0.3333333 0.4444444 0.5555556 0.6666667 0.7777778 0.8888889 1.0000000
[11] Inf
19.4 Conditional execution
An if statement allows you to conditionally execute
code. It looks like this:
# if (condition) {
# code executed when condition is TRUE
# } else {
# code executed when condition is FALSE
# }
Define a function to check if an object has names
has_name <- function(x){
nms <- names(x)
if(is.null(nms)){
rep(FALSE,length(x))
}else {
!is.na(nms) & nms !=""
}
}
19.4.1 Conditions
how if condition works with warnings
# if (c(TRUE,FALSE)){}
#> Warning in if (c(TRUE, FALSE)) {: the condition has length > 1 and only the
#> first element will be used
#> NULL
# if (NA) {}
Check if two objects are identical
identical(0L,0)
[1] FALSE
x <- sqrt(2)^2
x==2
[1] FALSE
x-2
[1] 4.440892e-16
19.4.2 Multiple conditions
You can chain multiple if statement together:
# if (this) {
# # do that
# } else if (that) {
# # do something else
# } else {
# #
# }
#> function(x, y, op) {
#> switch(op,
#> plus = x + y,
#> minus = x - y,
#> times = x * y,
#> divide = x / y,
#> stop("Unknown op!")
#> )
#> }
19.4.3 Code style
Good practice for writing if statements
# Good
# if (y < 0 && debug) {
# message("Y is negative")
# }
#
# if (y == 0) {
# log(x)
# } else {
# y ^ x
# }
#
# # Bad
# if (y < 0 && debug)
# message("Y is negative")
#
# if (y == 0) {
# log(x)
# }
# else {
# y ^ x
# }
It’s ok to drop the curly braces if you have a very short if
statement that can fit on one line:
y <- 10
x <- if (y < 20) "Too low" else "Too high"
I recommend this only for very brief if statements.
Otherwise, the full form is easier to read:
if (y < 20) {
x <- "Too low"
} else {
x <- "Too high"
}
19.5 Function arguments
# Compute confidence interval around mean using normal approximation
mean_ci <- function(x, conf = 0.95) {
se <- sd(x) / sqrt(length(x))
alpha <- 1 - conf
mean(x) + se * qnorm(c(alpha / 2, 1 - alpha / 2))
}
x <- runif(100)
mean_ci(x)
[1] 0.4370008 0.5485763
mean_ci(x, conf = 0.99)
[1] 0.4194710 0.5661061
19.5.1 Choosing names
19.5.2 Cheking values
wt_mean <- function(x, w) {
sum(x * w) / sum(w)
}
wt_var <- function(x, w) {
mu <- wt_mean(x, w)
sum(w * (x - mu) ^ 2) / sum(w)
}
wt_sd <- function(x, w) {
sqrt(wt_var(x, w))
}
What happens if x and w are not the same length?
wt_mean(1:6, 1:3)
[1] 7.666667
In this case, because of R’s vector recycling rules, we don’t get an
error.
It’s good practice to check important preconditions, and throw an
error (with stop()), if they are not true:
wt_mean <- function(x, w) {
if (length(x) != length(w)) {
stop("`x` and `w` must be the same length", call. = FALSE)
}
sum(w * x) / sum(w)
}
wt_mean <- function(x, w, na.rm = FALSE) {
if (!is.logical(na.rm)) {
stop("`na.rm` must be logical")
}
if (length(na.rm) != 1) {
stop("`na.rm` must be length 1")
}
if (length(x) != length(w)) {
stop("`x` and `w` must be the same length", call. = FALSE)
}
if (na.rm) {
miss <- is.na(x) | is.na(w)
x <- x[!miss]
w <- w[!miss]
}
sum(w * x) / sum(w)
}
This is a lot of extra work for little additional gain. A useful
compromise is the built-in stopifnot(): it checks that each
argument is TRUE, and produces a generic error message if
not.
wt_mean <- function(x, w, na.rm = FALSE) {
stopifnot(is.logical(na.rm), length(na.rm) == 1)
stopifnot(length(x) == length(w))
if (na.rm) {
miss <- is.na(x) | is.na(w)
x <- x[!miss]
w <- w[!miss]
}
sum(w * x) / sum(w)
}
19.5.3 Dot-dot-dot(…)
Many functions in R take an arbitrary number of inputs:
sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
[1] 55
stringr::str_c("a", "b", "c", "d", "e", "f")
[1] "abcdef"
Define a function to concatenate strings with commas
commas <- function(...) stringr::str_c(..., collapse = ", ")
commas(letters[1:10])
[1] "a, b, c, d, e, f, g, h, i, j"
rule <- function(..., pad = "-") {
title <- paste0(...)
width <- getOption("width") - nchar(title) - 5
cat(title, " ", stringr::str_dup(pad, width), "\n", sep = "")
}
rule("Important output")
Important output ----------------------------------------------------------------------------------------
x <- c(1,2)
sum(x,na.rm=T)
[1] 3
Define a function ‘complicated_function’ with conditions to return 0
if ‘x’ or ‘y’ is empty
complicated_function <- function(x,y,z){
if (lenth(x)==0 || length(y)==0){
return(0)
}
}
Improve readability of if-else blocks by using early return for
simple cases
f <- function() {
if (x) {
# Do
# something
# that
# takes
# many
# lines
# to
# express
} else {
# return something short
}
}
But if the first block is very long, by the time you get to the else,
you’ve forgotten the condition. One way to rewrite it is to use an early
return for the simple case:
f <- function() {
if (!x) {
return(something_short)
}
# Do
# something
# that
# takes
# many
# lines
# to
# express
}
This tends to make the code easier to understand, because you don’t
need quite so much context to understand it.
19.6.2 Writing pipeable functions
Define a function to show the count of missing values in a data
frame
show_missing <- function(df){
n <- sum(is.na(df))
cat("Missing values:",n,"\n",sep="")
invisible(df)
}
If we call it interatively, the invisible() means that
the input df does not get printed out:
show_missing(mtcars)
Missing values:0
But it’s still there, it’s not just printed by default:
x <- show_missing(mtcars)
Missing values:0
class(x)
[1] "data.frame"
dim(x)
[1] 32 11
And we can still use it in a pipe:
library(magrittr)
library(tidyverse)
mtcars %>%
show_missing() %>%
mutate(mpg=ifelse(mpg<20,NA,mpg)) %>%
show_missing()
Missing values:0
Missing values:18
19.7 Environment
Define a function ‘f’ that takes an argument ‘x’ and returns the sum
of ‘x’ and ‘y’
f <- function(x){
x+y
}
Demonstrate how changing the value of ‘y’ affects the result of
calling function ‘f’
y <- 100
f(10)
[1] 110
y <- 1000
f(10)
[1] 1010
Overload the ‘+’ operator to behave differently based on a random
condition
`+` <- function(x, y) {
if (runif(1) < 0.1) {
sum(x, y)
} else {
sum(x, y) * 1.1
}
}
table(replicate(1000, 1 + 2))
3 3.3
94 906
#>
#> 3 3.3
#> 100 900
rm(`+`)
Chapter 20: Vectors
20.1.1 PRerequisites
library(tidyverse)
20.2 Vector basics
Determine the data type of different vectors
typeof(letters)
[1] "character"
typeof(1:10)
[1] "integer"
Determine the length of a list and display its contents
x <- list("a","b",1:10)
length(x)
[1] 3
x
[[1]]
[1] "a"
[[2]]
[1] "b"
[[3]]
[1] 1 2 3 4 5 6 7 8 9 10
Demonstrate modulo operation and creation of logical vectors
1:10 %% 3 ==0
[1] FALSE FALSE TRUE FALSE FALSE TRUE FALSE FALSE TRUE FALSE
c(T,T,F,NA)
[1] TRUE TRUE FALSE NA
20.3.2 Numeric
Integer and double vectors are known collectively as numeric vectors.
In R, numbers are doubles by default. To make an integer, place an L
after the number:
typeof(1)
[1] "double"
typeof(1L)
[1] "integer"
1.5
[1] 1.5
Demonstrate the behavior of floating point arithmetic
x <- sqrt(2)^2
x
[1] 2
x-2
[1] 4.440892e-16
Demonstrate the behavior of division by zero
c(-1,0,1)%/% 0
[1] -Inf NaN Inf
# [1] -Inf NaN Inf
20.3.3 Character
Determine the memory size of a string and a replicated string
vector
x <- "This is a reasonably long string."
pryr::object_size(x)
152 B
y <- rep(x,1000)
pryr::object_size(y)
8.14 kB
20.3.4 Missing values
Note that each type of atomic vector has its own missing value:
NA # logical
[1] NA
NA_integer_ # integer
[1] NA
NA_real_ # double
[1] NA
NA_character_ # character
[1] NA
Calculate the number and proportion of elements in a vector greater
than 10
x <- sample(20,100,replace=T)
y <- x > 10
sum(y) # how many are greater than 10?
[1] 49
mean(y) # what proportion are greater than 10?
[1] 0.49
if (length(x)){
}
NULL
Determine the data type of different vectors
typeof(c(TRUE,1L))
[1] "integer"
typeof(c(1L,1.5))
[1] "double"
typeof(c(1.5,"a"))
[1] "character"
Generate random numeric or logical vectors
sample(10)+100
[1] 110 103 104 101 102 107 109 106 105 108
runif(10)>0.5
[1] FALSE FALSE FALSE TRUE FALSE FALSE FALSE FALSE TRUE TRUE
Demonstrate vector arithmetic with vectors of different lengths
1:10 +1:2
[1] 2 4 4 6 6 8 8 10 10 12
1:10+1:3
Warning: longer object length is not a multiple of shorter object length
[1] 2 4 6 5 7 9 8 10 12 11
Create a tibble with two columns, ‘x’ and ‘y’, with different
lengths
library(tidyverse)
tibble(
x=1:4,
y=rep(1:2,each=2)
)
20.4.4 Naming vectors
All types of vectors can be named. You can name them during creatin
with c():
c(x=1,y=2,z=4)
x y z
1 2 4
Or after the fact with purr::set_names()
set_names(1:3,c("a","b","c"))
a b c
1 2 3
Named vectors are most useful for subsetting, described next.
20.4.5 Subsetting
Demonstrate subsetting vectors with integer indices
x <- c("one","two","three","four","five")
x[c(3,2,5)]
[1] "three" "two" "five"
By repeating a position, you can actually make a longer output than
input:
x[c(1,1,5,5,5,2)]
[1] "one" "one" "five" "five" "five" "two"
Negative values drop the elements at the specified positions:
x[c(-1,-3,-5)]
[1] "two" "four"
The error message mentions subsetting with zero, which returns no
values:
x[0]
character(0)
library(tidyverse)
x <- c(10,3,NA,5,8,1)
# tibble test
x <- as.tibble(x,ncol=1)
Warning: `as.tibble()` was deprecated in tibble 2.0.0.
Please use `as_tibble()` instead.
The signature and semantics have changed, see `?as_tibble`.
names(x)="v1"
is.na(x)
v1
[1,] FALSE
[2,] FALSE
[3,] TRUE
[4,] FALSE
[5,] FALSE
[6,] FALSE
x %>% filter(v1 == NA)
# all non-missing values of x
x <- c(10,3,NA,5,8,1)
x[!is.na(x)]
[1] 10 3 5 8 1
# all even (or missing) values of x
x[x %% 2==0]
[1] 10 NA 8
- If you have a named vector, you can subset it with a character
vector:
x <- c(abc=1, def=2,xyz=5)
x[c("xyz","def")]
xyz def
5 2
20.5 Recursive vectors (lists)
Create a list with numeric elements
x <- list(1,2,3)
x
[[1]]
[1] 1
[[2]]
[1] 2
[[3]]
[1] 3
Display the structure of lists with and without names
str(x)
List of 3
$ : num 1
$ : num 2
$ : num 3
x_named <- list(a=1,b=2,c=3)
str(x_named)
List of 3
$ a: num 1
$ b: num 2
$ c: num 3
Unlike atomic vectors, list() can contain a mix of
objects:
y <- list("a",1L,1.5,T)
str(y)
List of 4
$ : chr "a"
$ : int 1
$ : num 1.5
$ : logi TRUE
List can even contain other lists!
z <- list(list(1,2),list(3,4))
str(z)
List of 2
$ :List of 2
..$ : num 1
..$ : num 2
$ :List of 2
..$ : num 3
..$ : num 4
20.5.1 Visualizing lists
x1 <- list(c(1,2),c(3,4))
x2 <- list(list(1,2),list(3,4))
x3 <- list(1,list(2,list(3)))
x1
[[1]]
[1] 1 2
[[2]]
[1] 3 4
x2
[[1]]
[[1]][[1]]
[1] 1
[[1]][[2]]
[1] 2
[[2]]
[[2]][[1]]
[1] 3
[[2]][[2]]
[1] 4
x3
[[1]]
[1] 1
[[2]]
[[2]][[1]]
[1] 2
[[2]][[2]]
[[2]][[2]][[1]]
[1] 3
20.5.2 Subsetting
Create a list ‘a’ with named elements and demonstrate subsetting
a <- list(a = 1:3, b = "a string", c = pi, d = list(-1, -5))
str(a)
List of 4
$ a: int [1:3] 1 2 3
$ b: chr "a string"
$ c: num 3.14
$ d:List of 2
..$ : num -1
..$ : num -5
str(a[1:2])
List of 2
$ a: int [1:3] 1 2 3
$ b: chr "a string"
str(a[4])
List of 1
$ d:List of 2
..$ : num -1
..$ : num -5
Demonstrate subsetting lists using double square brackets
str(a[[1]])
int [1:3] 1 2 3
str(a[[4]])
List of 2
$ : num -1
$ : num -5
Access list elements by name using $ or [[ ]]
a$a
[1] 1 2 3
a[["a"]]
[1] 1 2 3
20.6 Attributes
Demonstrate setting and retrieving attributes of vectors
x <- 1:10
attr(x,"greeting")
NULL
attr(x,"greeting") <- "Hi!"
attr(x,"farewell") <- "Bye!"
attributes(x)
$greeting
[1] "Hi!"
$farewell
[1] "Bye!"
Demonstrate methods for class ‘Date’
as.Date
function (x, ...)
UseMethod("as.Date")
<bytecode: 0x0000022ea4225378>
<environment: namespace:base>
methods("as.Date")
[1] as.Date.character as.Date.default as.Date.factor as.Date.numeric as.Date.POSIXct
[6] as.Date.POSIXlt as.Date.vctrs_sclr* as.Date.vctrs_vctr*
see '?methods' for accessing help and source code
Retrieve specific methods for ‘as.Date’
getS3method("as.Date","default")
function (x, ...)
{
if (inherits(x, "Date"))
x
else if (is.null(x))
.Date(numeric())
else if (is.logical(x) && all(is.na(x)))
.Date(as.numeric(x))
else stop(gettextf("do not know how to convert '%s' to class %s",
deparse1(substitute(x)), dQuote("Date")), domain = NA)
}
<bytecode: 0x0000022eb85b7c20>
<environment: namespace:base>
getS3method("as.Date","numeric")
function (x, origin, ...)
if (missing(origin)) .Date(x) else as.Date(origin, ...) + x
<bytecode: 0x0000022ec2ac86e0>
<environment: namespace:base>
20.7.1 Factors
Demonstrate creating a factor and inspecting its attributes
x <- factor(c("ab","cd","ab"),levels=c("ab","cd","ed"))
typeof(x)
[1] "integer"
attributes(x)
$levels
[1] "ab" "cd" "ed"
$class
[1] "factor"
20.7.2 Dates and date-times
Dates in R are numeric vectors that represent the number of days
since 1 January 1970.
x <- as.Date("1971-01-01")
unclass(x)
[1] 365
typeof(x)
[1] "double"
attributes(x)
$class
[1] "Date"
Demonstrate creating and inspecting a date-time object
x <- lubridate::ymd_hm("1970-01-01 01:00")
unclass(x)
[1] 3600
attr(,"tzone")
[1] "UTC"
typeof(x)
[1] "double"
attributes(x)
$class
[1] "POSIXct" "POSIXt"
$tzone
[1] "UTC"
Demonstrate setting and retrieving time zone for date-time object
attr(x,"tzone") <- "US/Pacific"
x
[1] "1969-12-31 17:00:00 PST"
attr(x,"tzone") <- "US/Eastern"
x
[1] "1969-12-31 20:00:00 EST"
There is another type of date-times called POSIXIt. There are built
on top of named lists:
y <- as.POSIXlt(x)
typeof(y)
[1] "list"
#> [1] "list"
attributes(y)
$names
[1] "sec" "min" "hour" "mday" "mon" "year" "wday" "yday" "isdst" "zone" "gmtoff"
$class
[1] "POSIXlt" "POSIXt"
$tzone
[1] "US/Eastern" "EST" "EDT"
$balanced
[1] TRUE
20.7.3 Tibbles
Tibbles are augmented lists: they have class “tbl_df” + “tbl” +
“data.frame”, and names (column) and row.names
attributes:
tb <- tibble::tibble(x = 1:5, y = 5:1)
typeof(tb)
[1] "list"
attributes(tb)
$class
[1] "tbl_df" "tbl" "data.frame"
$row.names
[1] 1 2 3 4 5
$names
[1] "x" "y"
df <- data.frame(x = 1:5, y = 5:1)
typeof(df)
[1] "list"
attributes(df)
$names
[1] "x" "y"
$class
[1] "data.frame"
$row.names
[1] 1 2 3 4 5
Chapter 21: Iteration
21.1.1 Prerequisites
library(tidyverse)
21.2 For loops
Imagine we have this simple tibble:
df <- tibble(
a=rnorm(10),
b=rnorm(10),
c=rnorm(10),
d=rnorm(10)
)
Calculate the median for each column in a tibble
median(df$a)
[1] -0.3157254
median(df$b)
[1] -0.8006407
median(df$c)
[1] -0.2668019
median(df$d)
[1] -0.02814063
Calculate the median for each column in the data frame ‘df’ using a
for loop
df
output <- vector("double",ncol(df))
for (i in seq_along(df)){
output[[i]] <- median(df[[i]])
}
output <- tibble(output)
Demonstrate the behavior of seq_along and length functions with an
empty vector ‘y’
y <- vector("double", 0)
seq_along(y)
integer(0)
#> integer(0)
1:length(y)
[1] 1 0
#> [1] 1 0
21.3.1v Modifying an existing object
Sometimes, you want to use a for loop to modify an existing object.
For example, remember our challenges from functions. We wanted to
rescale every column in a data frame:
library(tidyverse)
df <- tibble(
a=rnorm(10),
b=rnorm(10),
c=rnorm(10),
d=rnorm(10)
)
rescale01 <- function(x){
rng <- range(x,na.rm=T)
(x-rng[1])/(rng[2]-rng[1])
}
df$a <- rescale01(df$a)
df$b <- rescale01(df$b)
df$c <- rescale01(df$c)
df$d <- rescale01(df$d)
df
for ( i in seq_along(df)){
df[[i]] <- rescale01(df[[i]])
}
21.3.2 Looping patterns
x
[1] "1969-12-31 20:00:00 EST"
results <- vector("list",length(x))
names(results) <- names(x)
Demonstrate looping patterns using a for loop to iterate over a list
‘x’ and store results in a list ‘results’
for(i in seq_along(x)){
name <- names(x)[[i]]
value <- x[[i]]
}
21.3.3 Unknown output length
Create a vector ‘output’ with unknown length and store results from a
for loop in it
means <- c(0,1,2)
output <- double()
for (i in seq_along(means)){
n <- sample(100,1)
output <- c(output,rnorm(n,means[[i]]))
}
str(output)
num [1:223] 2.4083 1.5499 0.6081 0.0844 0.7443 ...
output
[1] 2.40834490 1.54985893 0.60813582 0.08444820 0.74433326 0.23589873 0.13677913 -1.51138770
[9] -1.27301392 -1.76413099 -0.63497070 1.54956856 1.50375944 0.71571312 0.57801330 0.33952611
[17] 0.55112157 0.17114550 -0.45463725 1.16554397 0.69994812 -1.38517572 -0.21089332 0.59729886
[25] 0.96649672 0.27565281 2.07450311 0.94767106 -1.19450592 -1.17615918 -0.06135937 0.31565475
[33] 0.46863199 -2.44533524 1.06774440 -0.53263928 0.79354070 -1.03657232 -1.41232073 -1.32268012
[41] -0.80619868 -1.48689463 -1.54482571 1.03872808 -1.69903338 -1.03393281 1.63922764 1.21681751
[49] -1.50423215 0.08619177 -0.64176595 -0.43528690 2.35908464 -0.07057289 0.79716367 1.39285456
[57] 1.99268652 1.96561675 4.02439840 2.43533373 1.71102597 -1.18110259 0.59191107 1.10348971
[65] 0.73300412 0.12159976 0.27290089 1.61147640 0.05298932 0.28489074 0.51530326 2.48330465
[73] 1.37412695 1.25174310 1.51181660 0.51833391 1.22731542 0.94751275 2.14534938 3.73603533
[81] 2.50448575 -0.07692596 0.38360741 1.13672743 1.59343745 1.59795077 1.43992598 1.32878827
[89] 4.19537063 1.71172058 1.10573970 0.60868714 1.07205788 2.30309804 0.89510610 1.53102920
[97] 1.34831205 1.42881339 1.98257191 1.81541903 1.01326728 0.19392951 0.33627612 0.47158036
[105] 2.83906013 0.38940341 1.87692593 0.86561042 1.79769711 1.32415638 1.66865453 2.99697358
[113] 0.46228139 0.33591193 0.24631821 -0.43897896 -0.11946077 1.11951899 2.53562810 1.15236359
[121] -0.09132156 -1.16767499 0.69617045 1.53977593 -0.25021887 2.18171198 1.97165693 -0.23020504
[129] -0.44086089 1.15684137 2.24634334 1.55135055 2.31883196 0.67556263 0.08018167 1.13714558
[137] 1.91669266 1.11139134 2.44770118 0.19471109 2.17871196 4.23369053 -0.15692858 2.21704642
[145] 2.90250569 -0.47232133 1.53732998 0.24186549 2.47811777 3.33046579 2.23734182 0.96433477
[153] 2.53867102 2.31206058 1.97274207 2.13828221 2.57547609 1.13761332 1.18604257 1.61607018
[161] 2.37121047 2.84795864 3.27669950 1.60560488 2.10355804 3.13176141 3.26092255 0.75913241
[169] 0.30603003 2.52789744 0.60599780 2.73159671 0.64479899 -0.11114226 0.61712535 3.05991705
[177] 1.35211904 1.02472332 1.29546010 2.25444890 1.57803759 0.76376634 -0.73903589 2.93147906
[185] 2.07135777 1.17651876 1.52535341 1.73003413 0.57964605 1.54963744 2.16950772 1.45261534
[193] 2.78626480 2.75094794 2.99609585 2.54613765 1.71969902 4.22074008 2.97537992 1.47012235
[201] 1.27664953 2.99738151 2.29757845 1.24353502 2.34876874 0.40451196 0.98056608 2.15815198
[209] 1.21343698 2.37080467 1.56698860 3.19657275 0.47615986 0.53829438 3.06730806 1.80701539
[217] 2.43974635 1.12710760 2.72351352 2.57993773 3.26991511 1.74089217 0.42477812
Create a list ‘out’ with unknown length and store results from a for
loop in it
out <- vector("list",length(means))
for (i in seq_along(means)){
n <- sample(100,1)
out[[i]] <- rnorm(n,means[[i]])
}
str(out)
List of 3
$ : num [1:23] 0.109 0.669 -0.159 -0.325 -0.81 ...
$ : num [1:97] 1.57 2.15 2.41 1.75 1.1 ...
$ : num [1:9] 1.86 2.62 2.25 1.62 2.24 ...
str(unlist(out))
num [1:129] 0.109 0.669 -0.159 -0.325 -0.81 ...
21.3.4 Unknown sequence length
A while loop is also more general than a for loop, because you can
rewrite any for loop as a while loop, but you can’t rewrite every while
loop as for loop:
for (i in seq_along(x)) {
# body
}
# Equivalent to
i <- 1
while (i <= length(x)) {
# body
i <- i + 1
}
Herhow we could use a while loop to find how many tries it takes to
get three heads in a row:
flip <- function() sample(c("T", "H"), 1)
flips <- 0
nheads <- 0
while (nheads < 3) {
if (flip() == "H") {
nheads <- nheads + 1
} else {
nheads <- 0
}
flips <- flips + 1
}
flips
[1] 26
21.4 For loops vs. functionals
Compare for loop and functional approaches for calculating column
means in a data frame
df <- tibble(
a=rnorm(10),
b=rnorm(10),
c=rnorm(10),
d=rnorm(10)
)
Using for loop
output <- vector("double",length(df))
for (i in seq_along(df)){
output[[i]] <- mean(df[[i]])
}
output
[1] 0.476473102 0.001854536 0.558698854 0.220409290
Using functional approach with a custom function ‘col_mean’
col_mean <- function(df){
output <- vector("double",length(df))
for (i in seq_along(df)){
output[i] <- mean(df[[i]])
}
output
}
Define a function ‘col_median’ to calculate the median for each
column in the data frame ‘df’
col_median <- function(df){
output <- vector("double",hh(df))
for (i in seq_along(df)){
output[i] <- median(df[[i]])
}
output
}
col_sd <- function(df){
output <- vector("double",length(df))
for (i in seq_along(df)){
output[i] <- sd(df[[i]])
}
output
}
df
Define functions f1, f2, and f3 for calculating different powers of
absolute deviation from the mean
f1 <- function(x) abs(x-mean(x))^1
f2 <- function(x) abs(x-mean(x))^2
f3 <- function(x) abs(x-mean(x))^3
Define a function ‘f’ to calculate the absolute deviation from the
mean raised to a given power ‘i’
f <- function(x,i) abs(x-mean(x))^i
Define a function ‘col_summary’ to apply a summary function ‘fun’ to
each column of the data frame ‘df’
col_summary <- function(df, fun) {
out <- vector("double", length(df))
for (i in seq_along(df)) {
out[i] <- fun(df[[i]])
}
out
}
col_summary(df, median)
[1] 0.47327175 -0.06728873 0.35193999 0.23340748
col_summary(df, mean)
[1] 0.476473102 0.001854536 0.558698854 0.220409290
Demonstrate the use of ‘map_dbl’ from the ‘purrr’ package to apply a
function to each column of the data frame ‘df’
library(purrr)
head(df)
# Reference - for loop()
output <- vector("double",length(df))
for (i in seq_along(df)){
output[[i]] <- mean(df[[i]])
}
output
[1] 0.476473102 0.001854536 0.558698854 0.220409290
map_dbl(df,mean)
a b c d
0.476473102 0.001854536 0.558698854 0.220409290
map_dbl(df,median)
a b c d
0.47327175 -0.06728873 0.35193999 0.23340748
map_dbl(df,sd)
a b c d
1.1903576 0.9296956 0.7214505 0.6619177
df %>% map_dbl(mean)
a b c d
0.476473102 0.001854536 0.558698854 0.220409290
df %>% map_dbl(median)
a b c d
0.47327175 -0.06728873 0.35193999 0.23340748
df %>% map_dbl(sd)
a b c d
1.1903576 0.9296956 0.7214505 0.6619177
Demonstrate the use of ‘map_dbl’ from the ‘purrr’ package with
additional arguments
map_dbl(df,mean,trim=0.5)
a b c d
0.47327175 -0.06728873 0.35193999 0.23340748
Demonstrate the use of ‘map_int’ from the ‘purrr’ package to apply a
function that returns integers to each element of a list
z <- list(x=1:3,y=4:5)
z
$x
[1] 1 2 3
$y
[1] 4 5
map_int(z,length)
x y
3 2
21.5.1 Shortcuts
Demonstrate the use of ‘safely’ from the ‘purrr’ package to create a
safe version of a function
safe_log <- safely(log)
str(safe_log(10))
List of 2
$ result: num 2.3
$ error : NULL
str(safe_log("a"))
List of 2
$ result: NULL
$ error :List of 2
..$ message: chr "non-numeric argument to mathematical function"
..$ call : language .Primitive("log")(x, base)
..- attr(*, "class")= chr [1:3] "simpleError" "error" "condition"
Demonstrate the use of ‘map’ from the ‘purrr’ package with ‘safely’
to apply a safe version of a function to each element of a list
x <- list(1,10,"a")
y <- x %>% map(safely(log))
str(y)
List of 3
$ :List of 2
..$ result: num 0
..$ error : NULL
$ :List of 2
..$ result: num 2.3
..$ error : NULL
$ :List of 2
..$ result: NULL
..$ error :List of 2
.. ..$ message: chr "non-numeric argument to mathematical function"
.. ..$ call : language .Primitive("log")(x, base)
.. ..- attr(*, "class")= chr [1:3] "simpleError" "error" "condition"
Demonstrate the use of ‘transpose’ from the ‘purrr’ package to
transpose a list of lists
y <- x %>% transpose()
str(y)
List of 1
$ :List of 3
..$ : num 1
..$ : num 10
..$ : chr "a"
Demonstrate the use of error handling with ‘map_lgl’ and ‘is_null’
from the ‘purrr’ package
is_ok <- y$error %>% map_lgl(is_null)
x[!is_ok]
list()
# y$result[is_ok] %>% flatten_dbl()
Purrr provides two other useful adverbs:
x <- list(1,10,"a")
x %>% map_dbl(possibly(log,NA_real_))
[1] 0.000000 2.302585 NA
Demonstrate the use of ‘quietly’ from the ‘purrr’ package to suppress
errors and return results with warnings
x <- list(1,-1)
x %>% map(quietly(log)) %>% str()
List of 2
$ :List of 4
..$ result : num 0
..$ output : chr ""
..$ warnings: chr(0)
..$ messages: chr(0)
$ :List of 4
..$ result : num NaN
..$ output : chr ""
..$ warnings: chr "NaNs produced"
..$ messages: chr(0)
21.7 Mapping over multiple arguments
Generate random numbers from normal distributions with different
means using ‘map’ from the ‘purrr’ package
mu <- list(5,10,-3)
mu %>%
map(rnorm,n=5) %>%
str()
List of 3
$ : num [1:5] 5.11 4.32 5.23 5.09 4.36
$ : num [1:5] 9.66 9.56 12.07 9.6 7.39
$ : num [1:5] -2.57 -3.32 -4.2 -2.16 -2.32
Generate random numbers from normal distributions with different
means and standard deviations using ‘map2’ from the ‘purrr’ package
sigma <- list(1,5,10)
seq_along(mu) %>%
map(~rnorm(5,mu[[.]],sigma[[.]])) %>%
str()
List of 3
$ : num [1:5] 5.36 7.16 4.32 4.53 5.97
$ : num [1:5] 21.09 8.26 15.76 8.76 8.33
$ : num [1:5] -11.56 -11 -4.91 -10.91 -13.74
Define a custom ‘map2’ function to apply a binary function to
corresponding elements of two lists
map2(mu,sigma,rnorm,n=5) %>% str()
List of 3
$ : num [1:5] 4.85 4.27 4.85 3.68 3.97
$ : num [1:5] 15.4 16.3 13.5 11.2 11.4
$ : num [1:5] 4.561 -0.279 -11.559 0.298 -5.152
map2 <- function(x,y,f,...){
out <- vector("list",length(x))
for (i in seq_along(x)){
out[[i]] <- f(x[[i]],y[[i]],...)
}
out
}
Apply a function to corresponding elements of multiple lists using
‘pmap’ from the ‘purrr’ package
library(magrittr)
library(purrr)
n <- list(1,3,5)
args1 <- list(n,mu,sigma)
args1 %>%
pmap(rnorm) %>%
str()
List of 3
$ : num 3.71
$ : num [1:3] 6.11 12.73 9.31
$ : num [1:5] -12.562 -1.359 -0.823 -20.731 -12.676
Apply a function to corresponding elements of multiple lists with
named parameters using ‘pmap’ from the ‘purrr’ package
args2 <- list(mean=mu, sd=sigma,n=n)
args2 %>%
pmap(rnorm) %>%
str()
List of 3
$ : num 4.34
$ : num [1:3] 9.78 8.19 11.21
$ : num [1:5] 0.742 6.791 14.991 -1.91 0.511
Apply a function to corresponding rows of a data frame using ‘pmap’
from the ‘purrr’ package with a tibble
library(tidyverse)
parms <- tribble(
~mean,~sd,~n,
5,1,1,
10,5,3,
-3,10,5
)
parms %>%
pmap(rnorm)
[[1]]
[1] 3.243648
[[2]]
[1] 9.605242 11.025883 6.007252
[[3]]
[1] 16.669768 5.855557 -12.841618 -3.360844 5.139707
21.7.1 Involing different functions
Invoke different functions with different parameters using
‘invoke_map’ from the ‘purrr’ package
f <- c("runif","rnorm","rpois")
param <- list(
list(min=-1,max=1),
list(sd=5),
list(lambda=10)
)
f
[1] "runif" "rnorm" "rpois"
param
[[1]]
[[1]]$min
[1] -1
[[1]]$max
[1] 1
[[2]]
[[2]]$sd
[1] 5
[[3]]
[[3]]$lambda
[1] 10
To handle this case, you can use invoke_map():
invoke_map(f,param,n=5) %>%
str()
Warning: `invoke_map()` was deprecated in purrr 1.0.0.
Please use map() + exec() instead.
List of 3
$ : num [1:5] 0.7386 -0.9649 -0.0834 -0.4986 -0.9628
$ : num [1:5] 6.447 -6.531 -0.428 6.091 8.155
$ : int [1:5] 9 11 9 9 11
Invoke different functions with different parameters using ‘pmap’
from the ‘purrr’ package and a tibble
sim <- tribble(
~f, ~params,
"runif", list(min = -1, max = 1),
"rnorm", list(sd = 5),
"rpois", list(lambda = 10)
)
sim %>%
mutate(sim = invoke_map(f, params, n = 10))
21.8 Walk
Perform side effects without returning a value for each element of a
list using ‘walk’ from the ‘purrr’ package
x <- list(1,"a",3)
x %>%
walk(print)
[1] 1
[1] "a"
[1] 3
Perform side effects on each element of a list using ‘walk’ from the
‘purrr’ package, then save the results
library(ggplot2)
plots <- mtcars %>%
split(.$cyl) %>%
map(~ggplot(., aes(mpg, wt)) + geom_point())
paths <- stringr::str_c(names(plots), ".pdf")
pwalk(list(paths, plots), ggsave, path = tempdir())
Saving 7 x 7 in image
Retain or remove elements of a list based on a predicate function
using ‘keep’ and ‘discard’ from the ‘purrr’ package
iris %>%
keep(is.factor) %>%
str()
'data.frame': 150 obs. of 1 variable:
$ Species: Factor w/ 3 levels "setosa","versicolor",..: 1 1 1 1 1 1 1 1 1 1 ...
iris %>%
discard(is.factor) %>%
str()
'data.frame': 150 obs. of 4 variables:
$ Sepal.Length: num 5.1 4.9 4.7 4.6 5 5.4 4.6 5 4.4 4.9 ...
$ Sepal.Width : num 3.5 3 3.2 3.1 3.6 3.9 3.4 3.4 2.9 3.1 ...
$ Petal.Length: num 1.4 1.4 1.3 1.5 1.4 1.7 1.4 1.5 1.4 1.5 ...
$ Petal.Width : num 0.2 0.2 0.2 0.2 0.2 0.4 0.3 0.2 0.2 0.1 ...
library(tidyverse)
library(magrittr)
21.9.2 Reduce and accumulate
Iteratively combine elements of a list using a binary function with
‘reduce’ from the ‘purrr’ package
dfs <- list(
age=tibble(name="John",age=30),
sex=tibble(name=c("John","Mary"),sex=c("M","F")),
trt=tibble(name="Mary",treatment="A")
)
dfs %>% reduce(full_join)
Joining with `by = join_by(name)`Joining with `by = join_by(name)`
Find the intersection of multiple vectors using ‘reduce’ from the
‘purrr’ package
vs <- list(
c(1,3,5,6,10),
c(1,2,3,7,8,10),
c(1,2,3,4,8,9,10)
)
vs %>% reduce(intersect)
[1] 1 3 10
Iteratively apply a function to elements of a list using ‘accumulate’
from the ‘purrr’ package
x <- sample(10)
x
[1] 7 3 4 2 8 9 1 5 10 6
x %>% accumulate(`+`)
[1] 7 10 14 16 24 33 34 39 49 55
LS0tDQp0aXRsZTogIkludHJvZHVjdGlvbiB0byBSIg0KYXV0aG9yOiAiQmliZWsgU2Fwa290YSINCm91dHB1dDoNCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQNCi0tLQ0KDQojIFRpYmJsZXMNCg0KVGFzayAxOkxvYWRpbmcgdGhlIHRpZHl2ZXJzZSBwYWNrYWdlLg0KYGBge3J9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmBgYA0KVGFzayAyOkNvbnZlcnRpbmcgdGhlIGlyaXMgZGF0YXNldCB0byBhIHRpYmJsZS4NCmBgYHtyfQ0KYXNfdGliYmxlKGlyaXMpDQpgYGANClRhc2sgMzogQ3JlYXRpbmcgYSB0aWJibGUgd2l0aCBjb2x1bW5zICJ4LCIgInksIiBhbmQgInosIiB3aGVyZSAieCIgcmFuZ2VzIGZyb20gMSB0byA1LCAieSIgaXMgMSBmb3IgYWxsIHJvd3MsIGFuZCAieiIgaXMgY2FsY3VsYXRlZCBhcyB0aGUgc3F1YXJlIG9mICJ4IiBwbHVzICJ5IiBmb3IgZWFjaCByb3cuDQpgYGB7cn0NCnRpYmJsZSgNCiAgeCA9IDE6NSwgDQogIHkgPSAxLCANCiAgeiA9IHggXiAyICsgeQ0KKQ0KYGBgDQoNClRhc2sgNDpDcmVhdGluZyBhIHRpYmJsZSB3aXRoIGNvbHVtbnMgbmFtZWQgIjopIiAocmVwcmVzZW50aW5nICJzbWlsZSIpLCAiICIgKHJlcHJlc2VudGluZyAic3BhY2UiKSwgYW5kICIyMDAwIiAocmVwcmVzZW50aW5nICJudW1iZXIiKS4NCmBgYHtyfQ0KdGIgPC0gdGliYmxlKA0KICBgOilgID0gInNtaWxlIiwgDQogIGAgYCA9ICJzcGFjZSIsDQogIGAyMDAwYCA9ICJudW1iZXIiDQopDQp0Yg0KYGBgDQoNClRhc2sgNTpDcmVhdGluZyBhIHRpYmJsZSB3aXRoIGNvbHVtbnMgIngsIiAieSwiIGFuZCAieiwiIGNvbnRhaW5pbmcgdGhlIHZhbHVlcyAiYSwiIDIsIDMuNiBhbmQgImIsIiAxLCA4LjUgcmVzcGVjdGl2ZWx5Lg0KDQpgYGB7cn0NCnRyaWJibGUoDQogIH54LCB+eSwgfnosDQogIA0KICAiYSIsIDIsIDMuNiwNCiAgImIiLCAxLCA4LjUNCikNCmBgYA0KDQojIFRpYmJsZXMgdnMuIGRhdGEuZnJhbWUNCg0KVGFzay0xOkNyZWF0aW5nIGEgdGliYmxlIHdpdGggY29sdW1ucyAiYSwiICJiLCIgImMsIiAiZCwiIGFuZCAiZSwiIGNvbnRhaW5pbmcgMTAwMCByYW5kb21seSBnZW5lcmF0ZWQgdmFsdWVzIGZvciBlYWNoIGNvbHVtbiwgcmVwcmVzZW50aW5nIGRhdGVzLCBudW1iZXJzLCBhbmQgbGV0dGVycy4NCg0KYGBge3J9DQp0aWJibGUoDQogIGEgPSBsdWJyaWRhdGU6Om5vdygpICsgcnVuaWYoMWUzKSAqIDg2NDAwLA0KICBiID0gbHVicmlkYXRlOjp0b2RheSgpICsgcnVuaWYoMWUzKSAqIDMwLA0KICBjID0gMToxZTMsDQogIGQgPSBydW5pZigxZTMpLA0KICBlID0gc2FtcGxlKGxldHRlcnMsIDFlMywgcmVwbGFjZSA9IFRSVUUpDQopDQpgYGANClRhc2sgMjogVG5zdGFsbGluZyB0aGUgcGFja2FnZQ0KYGBge3J9DQpwYWNrYWdlX3RvX2luc3RhbGwgPC0gYygibnljZmxpZ2h0czEzIikNCg0KZm9yIChwYWNrYWdlX25hbWUgaW4gcGFja2FnZV90b19pbnN0YWxsKSB7DQogIGlmICghcmVxdWlyZU5hbWVzcGFjZShwYWNrYWdlX25hbWUsIHF1aWV0bHkgPSBUUlVFKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMocGFja2FnZV9uYW1lKQ0KICB9DQp9DQpsaWJyYXJ5KG55Y2ZsaWdodHMxMykNCmBgYA0KDQpUYXNrIDM6IFByaW50aW5nIHRoZSBmaXJzdCAxMCByb3dzIG9mIHRoZSBueWNmbGlnaHRzMTM6OmZsaWdodHMgZGF0YXNldCB3aXRoIHVubGltaXRlZCB3aWR0aC4NCmBgYHtyfQ0KbnljZmxpZ2h0czEzOjpmbGlnaHRzICU+JSANCiAgcHJpbnQobiA9IDEwLCB3aWR0aCA9IEluZikNCmBgYA0KDQoNClRhc2sgNDogVmlld2luZyB0aGUgbnljZmxpZ2h0czEzOjpmbGlnaHRzIGRhdGFzZXQgaW4gYSBzZXBhcmF0ZSB3aW5kb3cgZm9yIGludGVyYWN0aXZlIGV4cGxvcmF0aW9uLg0KYGBge3J9DQpueWNmbGlnaHRzMTM6OmZsaWdodHMgJT4lIA0KICBWaWV3KCkNCmBgYA0KDQojIyBTdWJzZXR0aW5nDQoNClRhc2sgMTogQ3JlYXRpbmcgYSB0aWJibGUgbmFtZWQgImRmIiB3aXRoIGNvbHVtbnMgIngiIGFuZCAieSwiIHRoZW4gYWNjZXNzaW5nIHRoZSAieCIgY29sdW1uIHVzaW5nIGRpZmZlcmVudCBtZXRob2RzOg0KYGBge3J9DQpkZiA8LSB0aWJibGUoDQogIHggPSBydW5pZig1KSwjZnVuY3Rpb24gdGhhdCBnZW5lcmF0ZXMgcmFuZG9tIG51bWJlcnMgZnJvbSBhIHVuaWZvcm0gZGlzdHJpYnV0aW9uDQogIHkgPSBybm9ybSg1KSAjIGZ1bmN0aW9uIHRoYXQgZ2VuZXJhdGVzIHJhbmRvbSBudW1iZXJzIGZyb20gYSBub3JtYWwgKEdhdXNzaWFuKSBkaXN0cmlidXRpb24NCikNCg0KZGYkeA0KDQpkZltbIngiXV0NCg0KZGZbWzFdXQ0KDQpkZiAlPiUgLiR4DQoNCg0KYGBgDQojIyBJbnRlcmFjdGluZyB3aXRoIG9sZGVyIGNvZGUNClRhc2stMTogRGV0ZXJtaW5pbmcgdGhlIGNsYXNzIG9mIHRoZSBvYmplY3QgInRiIiBhZnRlciBjb252ZXJ0aW5nIGl0IHRvIGEgZGF0YSBmcmFtZS4NCg0KYGBge3J9DQpjbGFzcyhhcy5kYXRhLmZyYW1lKHRiKSkNCmBgYA0KIyMgRXhlcmNpc2VzDQpUYXNrLTE6IEhvdyBjYW4geW91IHRlbGwgaWYgYW4gb2JqZWN0IGlzIGEgdGliYmxlPyAoSGludDogdHJ5IHByaW50aW5nIG10Y2Fycywgd2hpY2ggaXMgYSByZWd1bGFyIGRhdGEgZnJhbWUpLg0KYGBge3J9DQptdGNhcnMNCmBgYA0KVGFzay0yDQpgYGB7cn0NCiMgSW4gYSBkYXRhLmZyYW1lLCBleHRyYWN0aW5nIGEgbm9uLWV4aXN0ZW50IGNvbHVtbiByZXR1cm5zIE5VTEwsDQojIHdoZXJlYXMgaW4gYSB0aWJibGUsIGl0IHJhaXNlcyBhbiBlcnJvciwgcHJvdmlkaW5nIGltbWVkaWF0ZSBmZWVkYmFjay4NCiMgT3RoZXIgb3BlcmF0aW9ucywgc3VjaCBhcyBleHRyYWN0aW5nIGV4aXN0aW5nIGNvbHVtbnMgYW5kIHN1YnNldHMgb2YgY29sdW1ucywNCiMgYmVoYXZlIHNpbWlsYXJseSBhY3Jvc3MgYm90aCBkYXRhIGZyYW1lcyBhbmQgdGliYmxlcy4NCiMgVGhlIGRlZmF1bHQgYmVoYXZpb3Igb2YgZGF0YS5mcmFtZXMgbWF5IGxlYWQgdG8gZnJ1c3RyYXRpb24NCiMgZHVlIHRvIHRoZSBsYWNrIG9mIGVycm9yIGZlZWRiYWNrIGZvciBub24tZXhpc3RlbnQgY29sdW1ucywNCiMgcG90ZW50aWFsbHkgY2F1c2luZyB1bm5vdGljZWQgbWlzdGFrZXMgYW5kIGRpZmZpY3VsdHkgaW4gZGVidWdnaW5nLg0KIyBJbiBjb250cmFzdCwgdGliYmxlcyBvZmZlciBtb3JlIHJvYnVzdCBiZWhhdmlvciwgZW5oYW5jaW5nIGRhdGEgaW50ZWdyaXR5DQojIGFuZCBkZWJ1Z2dpbmcgZWZmaWNpZW5jeS4NCg0KZGYgPC0gZGF0YS5mcmFtZShhYmMgPSAxLCB4eXogPSAiYSIpDQoNCiMgRXh0cmFjdGluZyBub24tZXhpc3RlbnQgY29sdW1uIGluIGEgZGF0YS5mcmFtZQ0KZGYkeCAgIyBSZXR1cm5zIE5VTEwNCg0KIyBFeHRyYWN0aW5nIGV4aXN0aW5nIGNvbHVtbiBpbiBhIGRhdGEuZnJhbWUNCmRmWywgInh5eiJdICAjIFJldHVybnMgYSBkYXRhIGZyYW1lIHdpdGggb25lIGNvbHVtbiBjb250YWluaW5nIHRoZSB2YWx1ZXMgb2YgdGhlICJ4eXoiIGNvbHVtbg0KDQojIEV4dHJhY3RpbmcgbXVsdGlwbGUgY29sdW1ucyBpbiBhIGRhdGEuZnJhbWUNCmRmWywgYygiYWJjIiwgInh5eiIpXSAgIyBSZXR1cm5zIGEgZGF0YSBmcmFtZSBjb250YWluaW5nIG9ubHkgdGhlIHNwZWNpZmllZCBjb2x1bW5zDQoNCmBgYA0KVGFzay0zOklmIHlvdSBoYXZlIHRoZSBuYW1lIG9mIGEgdmFyaWFibGUgc3RvcmVkIGluIGFuIG9iamVjdCwgZS5nLiB2YXIgPC0gIm1wZyIsIGhvdyBjYW4geW91IGV4dHJhY3QgdGhlIHJlZmVyZW5jZSB2YXJpYWJsZSBmcm9tIGEgdGliYmxlPw0KDQoNCiMgTm8gcGFjYWthZ2VzDQpgYGB7cn0NCiMgaGVpZ2h0cyA8LSByZWFkX2NzdigiZGF0YS9oZWlnaHRzLmNzdiIpDQpgYGANCg0KVGFzayAxOiAgbGlzdGluZyBzZXZlcmFsIHRhYmxlczogdGFibGUxLCB0YWJsZTIsIHRhYmxlMywgdGFibGU0YSwgYW5kIHRhYmxlNGIuDQpgYGB7cn0NCnRhYmxlMQ0KdGFibGUyDQp0YWJsZTMNCnRhYmxlNGENCnRhYmxlNGINCmBgYA0KVGFzayAyOiBDYWxjdWxhdGluZyB0aGUgcmF0ZSBieSBkaXZpZGluZyB0aGUgbnVtYmVyIG9mIGNhc2VzIGJ5IHRoZSBwb3B1bGF0aW9uIGFuZCB0aGVuIG11bHRpcGx5aW5nIGJ5IDEwLDAwMCBmb3IgdGFibGUxLg0KYGBge3J9DQp0YWJsZTEgJT4lIA0KICBtdXRhdGUocmF0ZSA9IGNhc2VzIC8gcG9wdWxhdGlvbiAqIDEwMDAwKQ0KYGBgDQpUYXNrIDM6IENvdW50aW5nIHRoZSBvY2N1cnJlbmNlcyBvZiBlYWNoIHllYXIgaW4gdGFibGUxLCB1c2luZyB0aGUgJ2Nhc2VzJyBjb2x1bW4gYXMgdGhlIHdlaWdodC4NCmBgYHtyfQ0KdGFibGUxICU+JSANCiAgY291bnQoeWVhciwgd3QgPSBjYXNlcykNCmBgYA0KVGFzayA0OiBDcmVhdGluZyBhIGdncGxvdCB1c2luZyB0YWJsZTEsIHBsb3R0aW5nICd5ZWFyJyBhZ2FpbnN0ICdjYXNlcycgd2l0aCBsaW5lcyBncm91cGVkIGJ5ICdjb3VudHJ5JyBhbmQgY29sb3JlZCBpbiBncmV5NTAsIGFsb25nIHdpdGggcG9pbnRzIGNvbG9yZWQgYnkgJ2NvdW50cnknLg0KDQpgYGB7cn0NCmxpYnJhcnkoZ2dwbG90MikNCmdncGxvdCh0YWJsZTEsIGFlcyh5ZWFyLCBjYXNlcykpICsgDQogIGdlb21fbGluZShhZXMoZ3JvdXAgPSBjb3VudHJ5KSwgY29sb3VyID0gImdyZXk1MCIpICsgDQogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IGNvdW50cnkpKQ0KYGBgDQojICBQaXZvdGluZw0KIyMgTG9uZ2VyDQpUYXNrLTE6IHJlZmVycmluZyB0byAndGFibGU0YScNCmBgYHtyfQ0KdGFibGU0YQ0KYGBgDQpUYXNrLTI6IFJlc2hhcGluZyB0YWJsZTRhIHVzaW5nIHBpdm90X2xvbmdlciBmb3IgY29sdW1ucyAnMTk5OScgYW5kICcyMDAwJyBpbnRvICd5ZWFyJyBhbmQgJ2Nhc2VzJy4NCmBgYHtyfQ0KdGFibGU0YSAlPiUgDQogIHBpdm90X2xvbmdlcihjKGAxOTk5YCwgYDIwMDBgKSwgbmFtZXNfdG8gPSAieWVhciIsIHZhbHVlc190byA9ICJjYXNlcyIpDQpgYGANClRhc2stMzogUmVzaGFwaW5nIHRhYmxlNGIgd2l0aCBwaXZvdF9sb25nZXIgZm9yIGNvbHVtbnMgJzE5OTknIGFuZCAnMjAwMCcgaW50byAneWVhcicgYW5kICdwb3B1bGF0aW9uJy4NCmBgYHtyfQ0KdGFibGU0YiAlPiUgDQogIHBpdm90X2xvbmdlcihjKGAxOTk5YCwgYDIwMDBgKSwgbmFtZXNfdG8gPSAieWVhciIsIHZhbHVlc190byA9ICJwb3B1bGF0aW9uIikgICNmdW5jdGlvbiB0cmFuc2Zvcm1zIHdpZGUgZGF0YSBpbnRvIGxvbmcgZm9ybWF0IGJ5IHN0YWNraW5nIG11bHRpcGxlIGNvbHVtbnMgaW50byB0d286IG9uZSBmb3IgdmFyaWFibGUgbmFtZXMgYW5kIG9uZSBmb3IgdGhlaXIgY29ycmVzcG9uZGluZyB2YWx1ZXMNCmBgYA0KVGFzay00OiBjcmVhdGluZyB0aWR5IGRhdGFzZXRzIHRpZHk0YSBhbmQgdGlkeTRiIGJ5IHVzaW5nIHBpdm90X2xvbmdlciBvbiB0YWJsZTRhIGFuZCB0YWJsZTRiIHRvIHJlc2hhcGUgdGhlbS4gVGhlbiwgcGVyZm9ybWluZyBhIGxlZnQgam9pbiBvbiB0aWR5NGEgYW5kIHRpZHk0Yi4NCmBgYHtyfQ0KdGlkeTRhIDwtIHRhYmxlNGEgJT4lIA0KICBwaXZvdF9sb25nZXIoYyhgMTk5OWAsIGAyMDAwYCksIG5hbWVzX3RvID0gInllYXIiLCB2YWx1ZXNfdG8gPSAiY2FzZXMiKQ0KdGlkeTRiIDwtIHRhYmxlNGIgJT4lIA0KICBwaXZvdF9sb25nZXIoYyhgMTk5OWAsIGAyMDAwYCksIG5hbWVzX3RvID0gInllYXIiLCB2YWx1ZXNfdG8gPSAicG9wdWxhdGlvbiIpDQpsZWZ0X2pvaW4odGlkeTRhLCB0aWR5NGIpDQpgYGANCiMjIFdpZGVyDQpUYXNrLTE6RGlzcGxheWluZyB0YWJsZSAyDQpgYGB7cn0NCnRhYmxlMg0KYGBgDQoNClRhc2stMjogdXNpbmcgdGhlIHBpdm90X3dpZGVyIGZ1bmN0aW9uIG9uIHRhYmxlMiB0byB0cmFuc2Zvcm0gaXQgZnJvbSBsb25nIHRvIHdpZGUgZm9ybWF0LCB3aXRoICd0eXBlJyBiZWNvbWluZyB0aGUgbmV3IGNvbHVtbiBuYW1lcyBhbmQgJ2NvdW50JyBiZWluZyB0aGUgY29ycmVzcG9uZGluZyB2YWx1ZXMuDQpgYGB7cn0NCnRhYmxlMiAlPiUNCiAgICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gdHlwZSwgdmFsdWVzX2Zyb20gPSBjb3VudCkNCmBgYA0KIyMjIyMjIyMjIyMNCiMgU2VwYXJhdGluZyBhbmQgdW5pdGluZw0KIyMgU2VwYXJhdGUNClRhc2stMTpkaXNwbGF5aW5nIHRhYmxlMw0KYGBge3J9DQogdGFibGUzDQpgYGANCg0KVGFzay0yOiBVc2luZyB0aGUgc2VwYXJhdGUgZnVuY3Rpb24gb24gdGFibGUzIHNwbGl0cyB0aGUgJ3JhdGUnIGNvbHVtbiBpbnRvIHR3byBzZXBhcmF0ZSBjb2x1bW5zIG5hbWVkICdjYXNlcycgYW5kICdwb3B1bGF0aW9uJy4NCmBgYHtyfQ0KdGFibGUzICU+JSANCiAgc2VwYXJhdGUocmF0ZSwgaW50byA9IGMoImNhc2VzIiwgInBvcHVsYXRpb24iKSkNCmBgYA0KVGFzay0zOlVzaW5nIHRoZSBzZXBhcmF0ZSBmdW5jdGlvbiBvbiB0YWJsZTMgc3BsaXRzIHRoZSAncmF0ZScgY29sdW1uIGludG8gdHdvIHNlcGFyYXRlIGNvbHVtbnMgbmFtZWQgJ2Nhc2VzJyBhbmQgJ3BvcHVsYXRpb24nLCB1c2luZyB0aGUgJy8nIGNoYXJhY3RlciBhcyB0aGUgc2VwYXJhdG9yLg0KYGBge3J9DQp0YWJsZTMgJT4lIA0KICBzZXBhcmF0ZShyYXRlLCBpbnRvID0gYygiY2FzZXMiLCAicG9wdWxhdGlvbiIpLCBzZXAgPSAiLyIpDQpgYGANClRhc2stNDpVc2luZyB0aGUgc2VwYXJhdGUgZnVuY3Rpb24gb24gdGFibGUzIHNwbGl0cyB0aGUgJ3JhdGUnIGNvbHVtbiBpbnRvIHR3byBzZXBhcmF0ZSBjb2x1bW5zIG5hbWVkICdjYXNlcycgYW5kICdwb3B1bGF0aW9uJywgY29udmVydGluZyB0aGUgcmVzdWx0aW5nIGNvbHVtbnMgdG8gdGhlaXIgYXBwcm9wcmlhdGUgZGF0YSB0eXBlcy4NCmBgYHtyfQ0KdGFibGUzICU+JSANCiAgc2VwYXJhdGUocmF0ZSwgaW50byA9IGMoImNhc2VzIiwgInBvcHVsYXRpb24iKSwgY29udmVydCA9IFRSVUUpDQpgYGANClRhc2stNTogIEFwcGx5aW5nIHRoZSBzZXBhcmF0ZSBmdW5jdGlvbiB0byB0YWJsZTMsIHRoZSAneWVhcicgY29sdW1uIGlzIGRpdmlkZWQgaW50byB0d28gc2VwYXJhdGUgY29sdW1ucyBsYWJlbGVkICdjZW50dXJ5JyBhbmQgJ3llYXInLCB3aXRoIHRoZSBzZXBhcmF0b3IgZGVmaW5lZCBhcyB0aGUgc2Vjb25kIGNoYXJhY3Rlci4NCmBgYHtyfQ0KdGFibGUzICU+JSANCiAgc2VwYXJhdGUoeWVhciwgaW50byA9IGMoImNlbnR1cnkiLCAieWVhciIpLCBzZXAgPSAyKQ0KYGBgDQojIyBVbml0ZQ0KDQpUYXNrLTE6IFRoZSB1bml0ZSBmdW5jdGlvbiBpcyBhcHBsaWVkIHRvIHRhYmxlNSB0byBtZXJnZSB0aGUgJ2NlbnR1cnknIGFuZCAneWVhcicgY29sdW1ucyBpbnRvIGEgc2luZ2xlIGNvbHVtbiBuYW1lZCAnbmV3Jy4NCmBgYHtyfQ0KdGFibGU1ICU+JSANCiAgdW5pdGUobmV3LCBjZW50dXJ5LCB5ZWFyKQ0KYGBgDQpUYXNrLTI6IHVuaXRlIGZ1bmN0aW9uIGlzIGFwcGxpZWQgdG8gdGFibGU1IHRvIG1lcmdlIHRoZSAnY2VudHVyeScgYW5kICd5ZWFyJyBjb2x1bW5zIGludG8gYSBzaW5nbGUgY29sdW1uIG5hbWVkICduZXcnLCB3aXRoIG5vIHNlcGFyYXRvciBiZXR3ZWVuIHRoZW0uDQpgYGB7cn0NCnRhYmxlNSAlPiUgDQogIHVuaXRlKG5ldywgY2VudHVyeSwgeWVhciwgc2VwID0gIiIpDQpgYGANCiMgIE1pc3NpbmcgdmFsdWVzDQpUYXNrLTE6IENyZWF0ZSBhIHRpYmJsZSBuYW1lZCAic3RvY2tzIiB3aXRoIGNvbHVtbnMgInllYXIiLCAicXRyIiAocXVhcnRlciksIGFuZCAicmV0dXJuIiwgaGF2aW5nIGRhdGEgZm9yIDIwMTUgYW5kIDIwMTYsIHdpdGggcXVhcnRlcmx5IHJldHVybnMgc3BlY2lmaWVkIGFuZCBzb21lIG1pc3NpbmcgZW50cmllcyBhcyBOQS4NCmBgYHtyfQ0Kc3RvY2tzIDwtIHRpYmJsZSgNCiAgeWVhciAgID0gYygyMDE1LCAyMDE1LCAyMDE1LCAyMDE1LCAyMDE2LCAyMDE2LCAyMDE2KSwNCiAgcXRyICAgID0gYyggICAxLCAgICAyLCAgICAzLCAgICA0LCAgICAyLCAgICAzLCAgICA0KSwNCiAgcmV0dXJuID0gYygxLjg4LCAwLjU5LCAwLjM1LCAgIE5BLCAwLjkyLCAwLjE3LCAyLjY2KQ0KKQ0KYGBgDQoNCg0KVGFzay0yOlBpdm90aW5nIHRoZSAic3RvY2tzIiB0aWJibGUgdG8gd2lkZW4gdGhlIGRhdGEsIGV4dHJhY3RpbmcgY29sdW1ucyBmcm9tIHRoZSAieWVhciIgdmFyaWFibGUgYW5kIHZhbHVlcyBmcm9tIHRoZSAicmV0dXJuIiB2YXJpYWJsZS4NCmBgYHtyfQ0Kc3RvY2tzICU+JSANCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHllYXIsIHZhbHVlc19mcm9tID0gcmV0dXJuKQ0KYGBgDQoNClRhc2stMzogcGl2b3QgdGhlIGRhdGEgdG8gYSB3aWRlIGZvcm1hdCB3aXRoIGNvbHVtbnMgZm9yIGVhY2ggeWVhcidzIHJldHVybnMsIHRoZW4gcmVzaGFwZSBpdCBiYWNrIHRvIGEgbG9uZyBmb3JtYXQsIGtlZXBpbmcgb25seSB0aGUgbm9uLW1pc3NpbmcgdmFsdWVzIGluIHRoZSAicmV0dXJuIiBjb2x1bW4uDQpgYGB7cn0NCnN0b2NrcyAlPiUgDQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSB5ZWFyLCB2YWx1ZXNfZnJvbSA9IHJldHVybikgJT4lIA0KICBwaXZvdF9sb25nZXIoDQogICAgY29scyA9IGMoYDIwMTVgLCBgMjAxNmApLCANCiAgICBuYW1lc190byA9ICJ5ZWFyIiwgDQogICAgdmFsdWVzX3RvID0gInJldHVybiIsIA0KICAgIHZhbHVlc19kcm9wX25hID0gVFJVRQ0KICApDQpgYGANClRhc2stNDpGaWxsaW5nIG1pc3NpbmcgY29tYmluYXRpb25zIG9mICJ5ZWFyIiBhbmQgInF0ciIgaW4gdGhlICJzdG9ja3MiIGRhdGFzZXQuDQpgYGB7cn0NCnN0b2NrcyAlPiUgDQogIGNvbXBsZXRlKHllYXIsIHF0cikNCmBgYA0KVGFzay01OkNyZWF0aW5nIGEgdGliYmxlIG5hbWVkICJ0cmVhdG1lbnQiIGNvbnRhaW5pbmcgaW5mb3JtYXRpb24gYWJvdXQgaW5kaXZpZHVhbHMsIHRoZWlyIHRyZWF0bWVudCBncm91cHMsIGFuZCB0aGVpciByZXNwb25zZXMsIHdpdGggc29tZSBtaXNzaW5nIHZhbHVlcyBmb3IgdGhlICJwZXJzb24iIGNvbHVtbi4NCmBgYHtyfQ0KdHJlYXRtZW50IDwtIHRyaWJibGUoDQogIH4gcGVyc29uLCAgICAgICAgICAgfiB0cmVhdG1lbnQsIH5yZXNwb25zZSwNCiAgIkRlcnJpY2sgV2hpdG1vcmUiLCAxLCAgICAgICAgICAgNywNCiAgTkEsICAgICAgICAgICAgICAgICAyLCAgICAgICAgICAgMTAsDQogIE5BLCAgICAgICAgICAgICAgICAgMywgICAgICAgICAgIDksDQogICJLYXRoZXJpbmUgQnVya2UiLCAgMSwgICAgICAgICAgIDQNCikNCmBgYA0KVGFzay02OiBGaWxsaW5nIHRoZSBtaXNzaW5nIHZhbHVlcyBpbiB0aGUgInBlcnNvbiIgY29sdW1uIG9mIHRoZSAidHJlYXRtZW50IiB0aWJibGUuDQpgYGB7cn0NCnRyZWF0bWVudCAlPiUgDQogIGZpbGwocGVyc29uKQ0KDQpgYGANCg0KIyBDYXNlIFN0dWR5DQpUYXNrLTE6IExvYWRpbmcgZGF0YSBzZXQNCmBgYHtyfQ0Kd2hvDQpgYGANCg0KVGFzay0yOlBpdm90aW5nIHRoZSAid2hvIiBkYXRhc2V0IGZyb20gd2lkZSB0byBsb25nIGZvcm1hdCwgY29uZGVuc2luZyBjb2x1bW5zIGludG8gImNhc2VzIiBhbmQgY2FwdHVyaW5nIHRoZSBvcmlnaW5hbCBjb2x1bW4gbmFtZXMgaW4gImtleSIuDQpgYGB7cn0NCndobzEgPC0gd2hvICU+JSANCiAgcGl2b3RfbG9uZ2VyKA0KICAgIGNvbHMgPSBuZXdfc3BfbTAxNDpuZXdyZWxfZjY1LCANCiAgICBuYW1lc190byA9ICJrZXkiLCANCiAgICB2YWx1ZXNfdG8gPSAiY2FzZXMiLCANCiAgICB2YWx1ZXNfZHJvcF9uYSA9IFRSVUUNCiAgKQ0Kd2hvMQ0KYGBgDQpUYXNrLTM6Q291bnRpbmcgdGhlIG9jY3VycmVuY2VzIG9mIGVhY2ggImtleSIgaW4gdGhlICJ3aG8xIiBkYXRhc2V0Lg0KYGBge3J9DQogIHdobzEgJT4lIA0KICAgIGNvdW50KGtleSkNCmBgYA0KVGFzay00OlJlcGxhY2luZyAibmV3cmVsIiB3aXRoICJuZXdfcmVsIiBpbiB0aGUgImtleSIgY29sdW1uIG9mIHRoZSAid2hvMSIgZGF0YXNldCB0byBjcmVhdGUgIndobzIuIg0KYGBge3J9DQp3aG8yIDwtIHdobzEgJT4lIA0KICBtdXRhdGUoa2V5ID0gc3RyaW5ncjo6c3RyX3JlcGxhY2Uoa2V5LCAibmV3cmVsIiwgIm5ld19yZWwiKSkNCndobzINCmBgYA0KVGFzay01OlNlcGFyYXRpbmcgdGhlICJrZXkiIGNvbHVtbiBpbiB0aGUgIndobzIiIGRhdGFzZXQgaW50byAibmV3LCIgInR5cGUsIiBhbmQgInNleGFnZSIgY29sdW1ucyB1c2luZyAiXyIgYXMgdGhlIHNlcGFyYXRvciB0byBjcmVhdGUgIndobzMuIg0KYGBge3J9DQp3aG8zIDwtIHdobzIgJT4lIA0KICBzZXBhcmF0ZShrZXksIGMoIm5ldyIsICJ0eXBlIiwgInNleGFnZSIpLCBzZXAgPSAiXyIpDQp3aG8zDQpgYGANClRhc2stNjpDb3VudGluZyB0aGUgb2NjdXJyZW5jZXMgb2YgZWFjaCB1bmlxdWUgdmFsdWUgaW4gdGhlICJuZXciIGNvbHVtbiBvZiB0aGUgIndobzMiIGRhdGFzZXQuDQpgYGB7cn0NCndobzMgJT4lIA0KICBjb3VudChuZXcpDQpgYGANCg0KVGFzay03OlJlbW92aW5nIHRoZSAibmV3IiwgImlzbzIiLCBhbmQgImlzbzMiIGNvbHVtbnMgZnJvbSB0aGUgIndobzMiIGRhdGFzZXQgYW5kIGFzc2lnbmluZyB0aGUgcmVzdWx0IHRvICJ3aG80Ii4NCmBgYHtyfQ0Kd2hvNCA8LSB3aG8zICU+JSANCiAgc2VsZWN0KC1uZXcsIC1pc28yLCAtaXNvMykNCmBgYA0KDQpUYXNrLTg6U3BsaXR0aW5nIHRoZSAic2V4YWdlIiBjb2x1bW4gb2YgdGhlICJ3aG80IiBkYXRhc2V0IGludG8gInNleCIgYW5kICJhZ2UiIGNvbHVtbnMsIHNlcGFyYXRlZCBieSB0aGUgZmlyc3QgY2hhcmFjdGVyLCBhbmQgYXNzaWduaW5nIHRoZSByZXN1bHQgdG8gIndobzUiLg0KYGBge3J9DQp3aG81IDwtIHdobzQgJT4lIA0KICBzZXBhcmF0ZShzZXhhZ2UsIGMoInNleCIsICJhZ2UiKSwgc2VwID0gMSkNCndobzUNCmBgYA0KVGFzay05OlRyYW5zZm9ybWluZyB0aGUgIndobyIgZGF0YXNldCBmcm9tIHdpZGUgdG8gbG9uZyBmb3JtYXQsIGFkanVzdGluZyBjb2x1bW4gbmFtZXMsIGV4dHJhY3RpbmcgbWVhbmluZ2Z1bCB2YXJpYWJsZXMsIGRyb3BwaW5nIHVubmVjZXNzYXJ5IGNvbHVtbnMsIGFuZCBzcGxpdHRpbmcgdGhlICJzZXhhZ2UiIGNvbHVtbiBpbnRvICJzZXgiIGFuZCAiYWdlIi4NCmBgYHtyfQ0Kd2hvICU+JQ0KICBwaXZvdF9sb25nZXIoDQogICAgY29scyA9IG5ld19zcF9tMDE0Om5ld3JlbF9mNjUsIA0KICAgIG5hbWVzX3RvID0gImtleSIsIA0KICAgIHZhbHVlc190byA9ICJjYXNlcyIsIA0KICAgIHZhbHVlc19kcm9wX25hID0gVFJVRQ0KICApICU+JSANCiAgbXV0YXRlKA0KICAgIGtleSA9IHN0cmluZ3I6OnN0cl9yZXBsYWNlKGtleSwgIm5ld3JlbCIsICJuZXdfcmVsIikNCiAgKSAlPiUNCiAgc2VwYXJhdGUoa2V5LCBjKCJuZXciLCAidmFyIiwgInNleGFnZSIpKSAlPiUgDQogIHNlbGVjdCgtbmV3LCAtaXNvMiwgLWlzbzMpICU+JSANCiAgc2VwYXJhdGUoc2V4YWdlLCBjKCJzZXgiLCAiYWdlIiksIHNlcCA9IDEpDQoNCmBgYA0KDQoNCiMjIENILTEzOiBSZWxhdGlvbmFsIGRhdGENCg0KVGFzay0xOkxvZGluZyB0aGUgbGlicmFyaWVzDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShueWNmbGlnaHRzMTMpDQpgYGANCg0KIyMgbnljZmxpZ2h0czEzDQpUYXNrLTE6IGFpcmxpbmVzIGRhdGENCmBgYHtyfQ0KYWlybGluZXMNCmBgYA0KDQpUYXNrLTI6IGFpcnBvcnRzIGRhdGENCmBgYHtyfQ0KYWlycG9ydHMNCmBgYA0KVGFzay0zOiBwbGFuZXMgZGF0YQ0KYGBge3J9DQpwbGFuZXMgDQpgYGANClRhc2stNDogd2VhdGhlciBkYXRhDQpgYGB7cn0NCndlYXRoZXIgDQpgYGANCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQoNCiMgS2V5cyANClRhc2stMUNvdW50aW5nIHRoZSBvY2N1cnJlbmNlcyBvZiBlYWNoIHRhaWwgbnVtYmVyIGluIHRoZSAicGxhbmVzIiB0YWJsZSBhbmQgZmlsdGVyaW5nIGZvciB0aG9zZSB3aXRoIG1vcmUgdGhhbiBvbmUgb2NjdXJyZW5jZS4NCmBgYHtyfQ0KcGxhbmVzICU+JSANCiAgY291bnQodGFpbG51bSkgJT4lIA0KICBmaWx0ZXIobiA+IDEpDQpgYGANCg0KVGFzay0yOkNvdW50aW5nIHRoZSBvY2N1cnJlbmNlcyBvZiBlYWNoIGNvbWJpbmF0aW9uIG9mIHllYXIsIG1vbnRoLCBkYXksIGhvdXIsIGFuZCBvcmlnaW4gaW4gdGhlICJ3ZWF0aGVyIiB0YWJsZSBhbmQgZmlsdGVyaW5nIGZvciB0aG9zZSB3aXRoIG1vcmUgdGhhbiBvbmUgb2NjdXJyZW5jZS4NCmBgYHtyfQ0Kd2VhdGhlciAlPiUgDQogIGNvdW50KHllYXIsIG1vbnRoLCBkYXksIGhvdXIsIG9yaWdpbikgJT4lIA0KICBmaWx0ZXIobiA+IDEpDQpgYGANClRhc2stMzpDb3VudGluZyB0aGUgb2NjdXJyZW5jZXMgb2YgZWFjaCBjb21iaW5hdGlvbiBvZiB5ZWFyLCBtb250aCwgZGF5LCBhbmQgZmxpZ2h0IGluIHRoZSAiZmxpZ2h0cyIgdGFibGUgYW5kIGZpbHRlcmluZyBmb3IgdGhvc2Ugd2l0aCBtb3JlIHRoYW4gb25lIG9jY3VycmVuY2UuDQpgYGB7cn0NCmZsaWdodHMgJT4lIA0KICBjb3VudCh5ZWFyLCBtb250aCwgZGF5LCBmbGlnaHQpICU+JSANCiAgZmlsdGVyKG4gPiAxKQ0KYGBgDQpUYXNrLTQ6Q291bnRpbmcgdGhlIG9jY3VycmVuY2VzIG9mIGVhY2ggY29tYmluYXRpb24gb2YgeWVhciwgbW9udGgsIGRheSwgYW5kIHRhaWwgbnVtYmVyIGluIHRoZSAiZmxpZ2h0cyIgdGFibGUgYW5kIGZpbHRlcmluZyBmb3IgdGhvc2Ugd2l0aCBtb3JlIHRoYW4gb25lIG9jY3VycmVuY2UuDQpgYGB7cn0NCmZsaWdodHMgJT4lIA0KICBjb3VudCh5ZWFyLCBtb250aCwgZGF5LCB0YWlsbnVtKSAlPiUgDQogIGZpbHRlcihuID4gMSkNCmBgYA0KIyBNdXRhdGluZyBqb2lucw0KDQpUYXNrLTE6IENyZWF0aW5nIGEgc3Vic2V0IG9mIHRoZSAiZmxpZ2h0cyIgdGFibGUgbmFtZWQgImZsaWdodHMyIiBjb250YWluaW5nIGNvbHVtbnMgZnJvbSAieWVhciIgdG8gImRheSIsICJob3VyIiwgIm9yaWdpbiIsICJkZXN0IiwgInRhaWxudW0iLCBhbmQgImNhcnJpZXIiLg0KYGBge3J9DQpmbGlnaHRzMiA8LSBmbGlnaHRzICU+JSANCiAgc2VsZWN0KHllYXI6ZGF5LCBob3VyLCBvcmlnaW4sIGRlc3QsIHRhaWxudW0sIGNhcnJpZXIpDQpmbGlnaHRzMg0KYGBgDQpUYXNrLTI6UmVtb3ZpbmcgdGhlICJvcmlnaW4iIGFuZCAiZGVzdCIgY29sdW1ucyBmcm9tICJmbGlnaHRzMiIgdGFibGUgYW5kIHRoZW4gcGVyZm9ybWluZyBhIGxlZnQgam9pbiB3aXRoIHRoZSAiYWlybGluZXMiIHRhYmxlLCB1c2luZyB0aGUgImNhcnJpZXIiIGNvbHVtbiBhcyB0aGUga2V5IGZvciBtYXRjaGluZy4NCmBgYHtyfQ0KZmxpZ2h0czIgJT4lDQogIHNlbGVjdCgtb3JpZ2luLCAtZGVzdCkgJT4lIA0KICBsZWZ0X2pvaW4oYWlybGluZXMsIGJ5ID0gImNhcnJpZXIiKQ0KYGBgDQpUYXNrLTM6U2hvcnRlbmluZyB0aGUgY29tbWFuZCBieSByZW1vdmluZyAic2VsZWN0aW5nIiBhbmQgZGlyZWN0bHkgIm11dGF0aW5nIiB0aGUgIm5hbWUiIGNvbHVtbiB3aXRoIHRoZSBjb3JyZXNwb25kaW5nIGFpcmxpbmUgbmFtZXMgZnJvbSB0aGUgImFpcmxpbmVzIiB0YWJsZSBiYXNlZCBvbiB0aGUgImNhcnJpZXIiIGNvbHVtbi4NCmBgYHtyfQ0KZmxpZ2h0czIgJT4lDQogIHNlbGVjdCgtb3JpZ2luLCAtZGVzdCkgJT4lIA0KICBtdXRhdGUobmFtZSA9IGFpcmxpbmVzJG5hbWVbbWF0Y2goY2FycmllciwgYWlybGluZXMkY2FycmllcildKQ0KYGBgDQojICBVbmRlcnN0YW5kaW5nIGpvaW5zDQpUYXNrLTE6Q3JlYXRpbmcgdHdvIHRpYmJsZXMsICJ4IiBhbmQgInkiLCBlYWNoIHdpdGggYSAia2V5IiBjb2x1bW4gYW5kIGFuIGFzc29jaWF0ZWQgInZhbF94IiBvciAidmFsX3kiIGNvbHVtbiwgcmVzcGVjdGl2ZWx5Lg0KYGBge3J9DQp4IDwtIHRyaWJibGUoDQogIH5rZXksIH52YWxfeCwNCiAgICAgMSwgIngxIiwNCiAgICAgMiwgIngyIiwNCiAgICAgMywgIngzIg0KKQ0KeSA8LSB0cmliYmxlKA0KICB+a2V5LCB+dmFsX3ksDQogICAgIDEsICJ5MSIsDQogICAgIDIsICJ5MiIsDQogICAgIDQsICJ5MyINCikNCg0KeA0KeQ0KYGBgDQoNCiMjIElubmVyIGpvaW4NClRhc2stMTpKb2luaW5nIHRpYmJsZXMgYHhgIGFuZCBgeWAgdXNpbmcgYW4gaW5uZXIgam9pbiBvcGVyYXRpb24gYmFzZWQgb24gdGhlICJrZXkiIGNvbHVtbi4NCmBgYHtyfQ0KeCAlPiUgDQogIGlubmVyX2pvaW4oeSwgYnkgPSAia2V5IikNCmBgYA0KIyMgRHVwbGljYXRlIGtleXMNClRhc2stMTogSm9pbmluZyB0aWJibGUgeCB3aXRoIHRpYmJsZSB5IHVzaW5nIHRoZSBjb21tb24gY29sdW1uICJrZXkiLg0KYGBge3J9DQp4IDwtIHRyaWJibGUoDQogIH5rZXksIH52YWxfeCwNCiAgICAgMSwgIngxIiwNCiAgICAgMiwgIngyIiwNCiAgICAgMiwgIngzIiwNCiAgICAgMSwgIng0Ig0KKQ0KeSA8LSB0cmliYmxlKA0KICB+a2V5LCB+dmFsX3ksDQogICAgIDEsICJ5MSIsDQogICAgIDIsICJ5MiINCikNCmBgYA0KDQpUYXNrLTI6UGVyZm9ybWluZyBhIGxlZnQgam9pbiBiZXR3ZWVuIHRpYmJsZSBgeGAgYW5kIHRpYmJsZSBgeWAgYmFzZWQgb24gdGhlIGNvbW1vbiBjb2x1bW4gImtleSIuDQpgYGB7cn0NCmxlZnRfam9pbih4LCB5LCBieSA9ICJrZXkiKQ0KYGBgDQoNClRhc2stMzpDcmVhdGluZyB0d28gdGliYmxlcywgYHhgIGFuZCBgeWAsIHdpdGggY29sdW1ucyAia2V5IiwgInZhbF94IiwgYW5kICJ2YWxfeSIsIHBvcHVsYXRlZCB3aXRoIGNvcnJlc3BvbmRpbmcgdmFsdWVzLg0KYGBge3J9DQp4IDwtIHRyaWJibGUoDQogIH5rZXksIH52YWxfeCwNCiAgICAgMSwgIngxIiwNCiAgICAgMiwgIngyIiwNCiAgICAgMiwgIngzIiwNCiAgICAgMywgIng0Ig0KKQ0KeSA8LSB0cmliYmxlKA0KICB+a2V5LCB+dmFsX3ksDQogICAgIDEsICJ5MSIsDQogICAgIDIsICJ5MiIsDQogICAgIDIsICJ5MyIsDQogICAgIDMsICJ5NCINCikNCmBgYA0KDQpUYXNrLTQ6UGVyZm9ybWluZyBhIGxlZnQgam9pbiBvbiB0aWJibGVzIGB4YCBhbmQgYHlgIHVzaW5nIHRoZSAia2V5IiBjb2x1bW4gYXMgdGhlIGpvaW4ga2V5Lg0KYGBge3J9DQpsZWZ0X2pvaW4oeCwgeSwgYnkgPSAia2V5IikNCmBgYA0KIyBEZWZpbmluZyB0aGUga2V5IGNvbHVtbnMNClRhc2stMTpQZXJmb3JtaW5nIGEgbGVmdCBqb2luIGJldHdlZW4gdGhlIGBmbGlnaHRzMmAgdGliYmxlIGFuZCB0aGUgYHdlYXRoZXJgIHRpYmJsZS4NCmBgYHtyfQ0KZmxpZ2h0czIgJT4lIA0KICBsZWZ0X2pvaW4od2VhdGhlcikNCmBgYA0KVGFzay0yOlBlcmZvcm1pbmcgYSBsZWZ0IGpvaW4gYmV0d2VlbiB0aGUgYGZsaWdodHMyYCB0aWJibGUgYW5kIHRoZSBgcGxhbmVzYCB0aWJibGUgdXNpbmcgdGhlICJ0YWlsbnVtIiBjb2x1bW4gYXMgdGhlIGtleS4NCmBgYHtyfQ0KZmxpZ2h0czIgJT4lIA0KICBsZWZ0X2pvaW4ocGxhbmVzLCBieSA9ICJ0YWlsbnVtIikNCmBgYA0KVGFzay0zOlBlcmZvcm1pbmcgYSBsZWZ0IGpvaW4gYmV0d2VlbiB0aGUgYGZsaWdodHMyYCB0aWJibGUgYW5kIHRoZSBgYWlycG9ydHNgIHRpYmJsZSwgbWF0Y2hpbmcgdGhlICJkZXN0IiBjb2x1bW4gZnJvbSBgZmxpZ2h0czJgIHdpdGggdGhlICJmYWEiIGNvbHVtbiBmcm9tIGBhaXJwb3J0c2AuDQpgYGB7cn0NCmZsaWdodHMyICU+JSANCiAgbGVmdF9qb2luKGFpcnBvcnRzLCBjKCJkZXN0IiA9ICJmYWEiKSkNCmBgYA0KDQpUYXNrLTQ6UGVyZm9ybWluZyBhIGxlZnQgam9pbiBiZXR3ZWVuIHRoZSBgZmxpZ2h0czJgIHRpYmJsZSBhbmQgdGhlIGBhaXJwb3J0c2AgdGliYmxlLCBtYXRjaGluZyB0aGUgIm9yaWdpbiIgY29sdW1uIGZyb20gYGZsaWdodHMyYCB3aXRoIHRoZSAiZmFhIiBjb2x1bW4gZnJvbSBgYWlycG9ydHNgLg0KYGBge3J9DQpmbGlnaHRzMiAlPiUgDQogIGxlZnRfam9pbihhaXJwb3J0cywgYygib3JpZ2luIiA9ICJmYWEiKSkNCmBgYA0KIyAgRmlsdGVyaW5nIGpvaW5zDQpUYXNrLTE6IENhbGN1bGF0aW5nIHRoZSB0b3AgMTAgZGVzdGluYXRpb25zIGJ5IGNvdW50aW5nIHRoZSBvY2N1cnJlbmNlcyBpbiB0aGUgImRlc3QiIGNvbHVtbiBvZiB0aGUgYGZsaWdodHNgIHRpYmJsZSwgc29ydGVkIGluIGRlc2NlbmRpbmcgb3JkZXIsIGFuZCB0aGVuIGRpc3BsYXlpbmcgdGhlIHJlc3VsdC4NCmBgYHtyfQ0KdG9wX2Rlc3QgPC0gZmxpZ2h0cyAlPiUNCiAgY291bnQoZGVzdCwgc29ydCA9IFRSVUUpICU+JQ0KICBoZWFkKDEwKQ0KdG9wX2Rlc3QNCmBgYA0KVGFzay0yOiBGaWx0ZXJpbmcgdGhlIGBmbGlnaHRzYCB0aWJibGUgdG8gaW5jbHVkZSBvbmx5IHJvd3Mgd2hlcmUgdGhlIGRlc3RpbmF0aW9uIChgZGVzdGApIG1hdGNoZXMgYW55IG9mIHRoZSB0b3AgMTAgZGVzdGluYXRpb25zIGlkZW50aWZpZWQgaW4gdGhlIHByZXZpb3VzIHN0ZXAuDQpgYGB7cn0NCmZsaWdodHMgJT4lIA0KICBmaWx0ZXIoZGVzdCAlaW4lIHRvcF9kZXN0JGRlc3QpDQojJWluJSBvcGVyYXRvciBpbiBSIGlzIHVzZWQgdG8gY2hlY2sgaWYgZWxlbWVudHMgaW4gb25lIHZlY3RvciBhcmUgcHJlc2VudCBpbiBhbm90aGVyIHZlY3Rvcg0KYGBgDQoNClRhc2stMzogU2VsZWN0aW5nIHJvd3MgZnJvbSB0aGUgYGZsaWdodHNgIGRhdGFzZXQgd2hlcmUgdGhlIGRlc3RpbmF0aW9uIGFpcnBvcnQgbWF0Y2hlcyBvbmUgb2YgdGhlIHRvcCAxMCBkZXN0aW5hdGlvbnMgcHJldmlvdXNseSBpZGVudGlmaWVkLg0KYGBge3J9DQpmbGlnaHRzICU+JSANCiAgc2VtaV9qb2luKHRvcF9kZXN0KQ0KYGBgDQpUYXNrLTQ6IEZpbHRlcmluZyBvdXQgZmxpZ2h0cyB3aXRoIHRhaWwgbnVtYmVycyBwcmVzZW50IGluIHRoZSBwbGFuZXMgZGF0YXNldCBhbmQgY291bnRpbmcgdGhlIG9jY3VycmVuY2VzIG9mIGVhY2ggdW5pcXVlIHRhaWwgbnVtYmVyLCBzb3J0aW5nIHRoZSByZXN1bHQuDQpgYGB7cn0NCmZsaWdodHMgJT4lDQogIGFudGlfam9pbihwbGFuZXMsIGJ5ID0gInRhaWxudW0iKSAlPiUNCiAgY291bnQodGFpbG51bSwgc29ydCA9IFRSVUUpDQpgYGANCiMgU2V0IG9wZXJhdGlvbnMNClRhc2stMTpjcmVhdGluZyB0d28gdGliYmxlcywgZGYxIGFuZCBkZjIsIGVhY2ggd2l0aCBjb2x1bW5zIHggYW5kIHksIGNvbnRhaW5pbmcgc2FtcGxlIGRhdGEuDQpgYGB7cn0NCmRmMSA8LSB0cmliYmxlKA0KICB+eCwgfnksDQogICAxLCAgMSwNCiAgIDIsICAxDQopDQpkZjIgPC0gdHJpYmJsZSgNCiAgfngsIH55LA0KICAgMSwgIDEsDQogICAxLCAgMg0KKQ0KYGBgDQoNClRhc2stMjpwZXJmb3JtaW5nIHNldCBvcGVyYXRpb25zIG9uIHRoZSB0aWJibGVzIGRmMSBhbmQgZGYyLCBpbmNsdWRpbmcgaW50ZXJzZWN0aW9uLCB1bmlvbiwgYW5kIHNldCBkaWZmZXJlbmNlcy4NCmBgYHtyfQ0KaW50ZXJzZWN0KGRmMSwgZGYyKQ0KdW5pb24oZGYxLCBkZjIpDQpzZXRkaWZmKGRmMSwgZGYyKQ0Kc2V0ZGlmZihkZjIsIGRmMSkNCmBgYA0KDQojIENILTE0OiBTdHJpbmdzDQpCYXNpYyBJbmZvOnN0cmluZzEgPC0gIlRoaXMgaXMgYSBzdHJpbmciDQogICAgICAgICAgIHN0cmluZzIgPC0gJ0lmIEkgd2FudCB0byBpbmNsdWRlIGEgInF1b3RlIiBpbnNpZGUgYSBzdHJpbmcsIEkgdXNlIHNpbmdsZSBxdW90ZXMnDQogICAgICAgICAgIA0KVGFzay0xOlRvIGluY2x1ZGUgYSBsaXRlcmFsIHNpbmdsZSBvciBkb3VibGUgcXVvdGUgaW4gYSBzdHJpbmcgeW91IGNhbiB1c2UgXCB0byDigJxlc2NhcGXigJ0gaXQgICAgICAgICANCmBgYHtyfQ0KZG91YmxlX3F1b3RlIDwtICJcIiIgIyBvciAnIicNCnNpbmdsZV9xdW90ZSA8LSAnXCcnICMgb3IgIiciDQpgYGANCg0KVGFzay0yOiBVbmRlcnN0YW5kaW5nIHRoZSBjaGFyYWN0ZXIgDQpgYGB7cn0NCg0KeCA8LSBjKCJcIiIsICJcXCIpICNiYWNrc2xhc2ggaXMgZXNjYXBlIGNoYXJhY3Rlcg0KeA0Kd3JpdGVMaW5lcyh4KQ0KYGBgDQojICBTdHJpbmcgbGVuZ3RoDQpUYXNrLTE6DQpgYGB7cn0NCnN0cl9sZW5ndGgoYygiYSIsICJSIGZvciBkYXRhIHNjaWVuY2UiLCBOQSkpDQpgYGANCg0KIyBDb21iaW5pbmcgc3RyaW5ncw0KVGFzay0xOkNvbWJpbmluZyAgdGhlIHN0cmluZ3MNCmBgYHtyfQ0Kc3RyX2MoIngiLCAieSIpDQpzdHJfYygieCIsICJ5IiwgInoiKQ0KYGBgDQpUYXNrLTI6VXNpbmcgdGhlIHNlcCBhcmd1bWVudCB0byBjb250cm9sIGhvdyB0aGV54oCZcmUgc2VwYXJhdGVkLg0KYGBge3J9DQpzdHJfYygieCIsICJ5Iiwgc2VwID0gIiwgIikNCmBgYA0KVGFzay0zOlBlcmZvcm1pbmcgY29uY2F0ZW5hdGlvbiB3aXRoICJ8IiBhbmQgIi0iIGF0IGJvdGggZW5kcyBvZiBlYWNoIGVsZW1lbnQgb2YgdmVjdG9yIHgsIGFuZCByZXBsYWNpbmcgTkEgdmFsdWVzIHdpdGggZW1wdHkgc3RyaW5ncyBiZWZvcmUgY29uY2F0ZW5hdGlvbi4NCmBgYHtyfQ0KeCA8LSBjKCJhYmMiLCBOQSkNCnN0cl9jKCJ8LSIsIHgsICItfCIpDQpzdHJfYygifC0iLCBzdHJfcmVwbGFjZV9uYSh4KSwgIi18IikNCmBgYA0KVGFzay00OiBjb25jYXRlbmF0aW5nIGVhY2ggZWxlbWVudCBvZiB0aGUgdmVjdG9yIGMoImEiLCAiYiIsICJjIikgd2l0aCBhIHByZWZpeCAicHJlZml4LSIgYW5kIGEgc3VmZml4ICItc3VmZml4Ii4NCmBgYHtyfQ0Kc3RyX2MoInByZWZpeC0iLCBjKCJhIiwgImIiLCAiYyIpLCAiLXN1ZmZpeCIpDQpgYGANClRhc2stNTogY29tYmluaW5nIHN0cmluZ3MNCmBgYHtyfQ0KbmFtZSA8LSAiSGFkbGV5Ig0KdGltZV9vZl9kYXkgPC0gIm1vcm5pbmciDQpiaXJ0aGRheSA8LSBGQUxTRQ0KDQpzdHJfYygNCiAgIkdvb2QgIiwgdGltZV9vZl9kYXksICIgIiwgbmFtZSwNCiAgaWYgKGJpcnRoZGF5KSAiIGFuZCBIQVBQWSBCSVJUSERBWSIsDQogICIuIg0KKQ0KYGBgDQojIFN1YnNldHRpbmcgc3RyaW5ncw0KVGFzay0xOkV4dHJhY3RpbmcgdGhlIGZpcnN0IHRocmVlIGNoYXJhY3RlcnMgZnJvbSBlYWNoIGVsZW1lbnQgaW4gdGhlIHZlY3RvciBgeGAgdXNpbmcgYHN0cl9zdWJgLg0KYGBge3J9DQp4IDwtIGMoIkFwcGxlIiwgIkJhbmFuYSIsICJQZWFyIikNCnN0cl9zdWIoeCwgMSwgMykNCmBgYA0KVGFzay0yOm5lZ2F0aXZlIG51bWJlcnMgY291bnQgYmFja3dhcmRzIGZyb20gZW5kDQpgYGB7cn0NCnN0cl9zdWIoeCwgLTMsIC0xKQ0KYGBgDQpUYXNrLTM6dXNpbmcgdGhlIGFzc2lnbm1lbnQgZm9ybSBvZiBzdHJfc3ViKCkgdG8gbW9kaWZ5IHN0cmluZ3MNCmBgYHtyfQ0Kc3RyX3N1Yih4LCAxLCAxKSA8LSBzdHJfdG9fbG93ZXIoc3RyX3N1Yih4LCAxLCAxKSkNCngNCmBgYA0KDQojIExvY2FsZXMNClRhc2stMTpDaGFuZ2luZyB0aGUgY2FzZSANCmBgYHtyfQ0Kc3RyX3RvX3VwcGVyKGMoImkiLCAixLEiKSkNCnN0cl90b191cHBlcihjKCJpIiwgIsSxIiksIGxvY2FsZSA9ICJ0ciIpDQpgYGANClRhc2stMjpTb3J0aW5nIHRoZSBjaGFyYWN0ZXIgdmVjdG9yIHggYWxwaGFiZXRpY2FsbHkgdXNpbmcgdGhlIEVuZ2xpc2ggKGVuKSBsb2NhbGUgYW5kIHRoZSBIYXdhaWlhbiAoaGF3KSBsb2NhbGUuDQpgYGB7cn0NCnggPC0gYygiYXBwbGUiLCAiZWdncGxhbnQiLCAiYmFuYW5hIikNCnN0cl9zb3J0KHgsIGxvY2FsZSA9ICJlbiIpIA0Kc3RyX3NvcnQoeCwgbG9jYWxlID0gImhhdyIpIA0KYGBgDQojICBNYXRjaGluZyBwYXR0ZXJucyB3aXRoIHJlZ3VsYXIgZXhwcmVzc2lvbnMNCg0KIyMgQmFzaWMgbWF0Y2hlcw0KVGFzay0xOlNlYXJjaGluZyBmb3IgdGhlIHBhdHRlcm4gImFuIiB3aXRoaW4gZWFjaCBlbGVtZW50IG9mIGB4YCBhbmQgZGlzcGxheWluZyB0aGUgbWF0Y2hlcy4NCmBgYHtyfQ0KeCA8LSBjKCJhcHBsZSIsICJiYW5hbmEiLCAicGVhciIpDQpzdHJfdmlldyh4LCAiYW4iKQ0KYGBgDQoNClRhc2stMjpEaXNwbGF5aW5nIGVsZW1lbnRzIGluIGB4YCB3aGVyZSBhbnkgY2hhcmFjdGVyIGlzIGZvbGxvd2VkIGJ5ICJhIiBhbmQgdGhlbiBhbnkgY2hhcmFjdGVyLg0KYGBge3J9DQpzdHJfdmlldyh4LCAiLmEuIikNCmBgYA0KVGFzay0zIA0KYGBge3J9DQojIFRvIGNyZWF0ZSB0aGUgcmVndWxhciBleHByZXNzaW9uLCB3ZSBuZWVkIFxcDQpkb3QgPC0gIlxcLiINCg0KIyBCdXQgdGhlIGV4cHJlc3Npb24gaXRzZWxmIG9ubHkgY29udGFpbnMgb25lOg0Kd3JpdGVMaW5lcyhkb3QpDQoNCiMgQW5kIHRoaXMgdGVsbHMgUiB0byBsb29rIGZvciBhbiBleHBsaWNpdCAuDQpzdHJfdmlldyhjKCJhYmMiLCAiYS5jIiwgImJlZiIpLCAiYVxcLmMiKQ0KDQpgYGANClRhc2stNDogRGlzcGxheWluZyBlbGVtZW50cyBpbiBgeGAgd2hlcmUgdGhlIHNlcXVlbmNlICJcXCIgb2NjdXJzLg0KYGBge3J9DQp4IDwtICJhXFxiIg0Kd3JpdGVMaW5lcyh4KQ0KDQpzdHJfdmlldyh4LCAiXFxcXCIpDQpgYGANCiMjICBBbmNob3JzDQpUYXNrLTE6IERpc3BsYXlpbmcgZWxlbWVudHMgaW4gYHhgIHRoYXQgc3RhcnQgd2l0aCAiYSIgYW5kIGVuZCB3aXRoICJhIiByZXNwZWN0aXZlbHkuDQpgYGB7cn0NCnggPC0gYygiYXBwbGUiLCAiYmFuYW5hIiwgInBlYXIiKQ0Kc3RyX3ZpZXcoeCwgIl5hIikNCnN0cl92aWV3KHgsICJhJCIpDQpgYGANClRhc2stMjogSGlnaGxpZ2h0aW5nICJhcHBsZSIgb2NjdXJyZW5jZXMgaW4gYHhgIGFuZCBpbnN0YW5jZXMgd2hlcmUgaXQncyB0aGUgb25seSBjb250ZW50Lg0KYGBge3J9DQp4IDwtIGMoImFwcGxlIHBpZSIsICJhcHBsZSIsICJhcHBsZSBjYWtlIikNCnN0cl92aWV3KHgsICJhcHBsZSIpDQpzdHJfdmlldyh4LCAiXmFwcGxlJCIpDQpgYGANCiMjIENoYXJhY3RlciBjbGFzc2VzIGFuZCBhbHRlcm5hdGl2ZXMNCg0KVGFzay0xOiBWaXN1YWxpemluZyBwYXR0ZXJucyBtYXRjaGluZyAiYS5jIiwgImEqYyIsIGFuZCAiYSBjIiBpbiB0aGUgcHJvdmlkZWQgY2hhcmFjdGVyIHZlY3Rvci4NCmBgYHtyfQ0Kc3RyX3ZpZXcoYygiYWJjIiwgImEuYyIsICJhKmMiLCAiYSBjIiksICJhWy5dYyIpDQpzdHJfdmlldyhjKCJhYmMiLCAiYS5jIiwgImEqYyIsICJhIGMiKSwgIi5bKl1jIikNCnN0cl92aWV3KGMoImFiYyIsICJhLmMiLCAiYSpjIiwgImEgYyIpLCAiYVsgXSIpDQpgYGANClRhc2stMjogVmlzdWFsaXppbmcgcGF0dGVybnMgbWF0Y2hpbmcgImdyZXkiIG9yICJncmF5IiBpbiB0aGUgcHJvdmlkZWQgY2hhcmFjdGVyIHZlY3Rvci4NCmBgYHtyfQ0Kc3RyX3ZpZXcoYygiZ3JleSIsICJncmF5IiksICJncihlfGEpeSIpDQpgYGANCiMjIFJlcGV0aXRpb24NClRhc2stMTpJZGVudGlmeWluZyBwYXR0ZXJucyAiQ0MiIG9yICJDIiBpbiB0aGUgc3RyaW5nICIxODg4IGlzIHRoZSBsb25nZXN0IHllYXIgaW4gUm9tYW4gbnVtZXJhbHMNCmBgYHtyfQ0KeCA8LSAiMTg4OCBpcyB0aGUgbG9uZ2VzdCB5ZWFyIGluIFJvbWFuIG51bWVyYWxzOiBNRENDQ0xYWFhWSUlJIg0Kc3RyX3ZpZXcoeCwgIkNDPyIpDQpgYGANClRhc2stMjogVmlld2luZyB0aGUgcGF0dGVybiAiQ0MiDQpgYGB7cn0NCnN0cl92aWV3KHgsICJDQysiKQ0KYGBgDQpUYXNrLTM6IFZpZXdpbmcgdGhlIHBhdHRlcm4gIkNbTFhdKyINCmBgYHtyfQ0Kc3RyX3ZpZXcoeCwgJ0NbTFhdKycpDQpgYGANClRhc2stNDpWaWV3aW5nIHRoZSBwYXR0ZXJuICJDezJ9LEN7Mix9LGN7MiwzfSINCmBgYHtyfQ0Kc3RyX3ZpZXcoeCwgIkN7Mn0iKQ0Kc3RyX3ZpZXcoeCwgIkN7Mix9IikNCnN0cl92aWV3KHgsICJDezIsM30iKQ0KYGBgDQojIyBHcm91cGluZyBhbmQgYmFja3JlZmVyZW5jZXMNClRhc2stMTpHcm91cGluZw0KYGBge3J9DQpzdHJfdmlldyhmcnVpdCwgIiguLilcXDEiLCBtYXRjaCA9IFRSVUUpDQpgYGANCg0KIyMgRGV0ZWN0IG1hdGNoZXMNClRhc2stMTogQ2hlY2tpbmcgZm9yIHRoZSBwcmVzZW5jZSBvZiB0aGUgbGV0dGVyICJlIiBpbiBlYWNoIHdvcmQgDQpgYGB7cn0NCnggPC0gYygiYXBwbGUiLCAiYmFuYW5hIiwgInBlYXIiKQ0Kc3RyX2RldGVjdCh4LCAiZSIpDQpgYGANCg0KVGFzay0yOkNoZWNraW5nIGhvdyBtYW55IGNvbW1vbiB3b3JkcyBzdGFydCB3aXRoIHQNCmBgYHtyfQ0Kc3VtKHN0cl9kZXRlY3Qod29yZHMsICJedCIpKQ0KYGBgDQoNClRhc2stMzogQ2hlY2tpbmcgcHJvcG9ydGlvbiBvZiBjb21tb24gd29yZHMgZW5kIHdpdGggYSB2b3dlbA0KYGBge3J9DQptZWFuKHN0cl9kZXRlY3Qod29yZHMsICJbYWVpb3VdJCIpKQ0KYGBgDQpUYXNrLTQ6RmluZGluZyBhbGwgd29yZHMgY29udGFpbmluZyBhdCBsZWFzdCBvbmUgdm93ZWwsIGFuZCBuZWdhdGUNCmBgYHtyfQ0Kbm9fdm93ZWxzXzEgPC0gIXN0cl9kZXRlY3Qod29yZHMsICJbYWVpb3VdIikNCmBgYA0KDQpUYXNrLTU6RmluZGluZyBhbGwgd29yZHMgY29uc2lzdGluZyBvbmx5IG9mIGNvbnNvbmFudHMgKG5vbi12b3dlbHMpDQpgYGB7cn0NCm5vX3Zvd2Vsc18yIDwtIHN0cl9kZXRlY3Qod29yZHMsICJeW15hZWlvdV0rJCIpDQppZGVudGljYWwobm9fdm93ZWxzXzEsIG5vX3Zvd2Vsc18yKQ0KYGBgDQoNClRhc2stNjogRmlsdGVyaW5nIHdvcmRzIHRoYXQgZW5kIHdpdGggdGhlIGxldHRlciAieCIgZnJvbSBhIGxpc3Qgb2Ygd29yZHMuDQpgYGB7cn0NCndvcmRzW3N0cl9kZXRlY3Qod29yZHMsICJ4JCIpXQ0Kc3RyX3N1YnNldCh3b3JkcywgIngkIikNCmBgYA0KVGFzay03OiBGaWx0ZXJpbmcgYSB0aWJibGUgZm9yIHdvcmRzIHRoYXQgZW5kIHdpdGggIngiLg0KYGBge3J9DQpkZiA8LSB0aWJibGUoDQogIHdvcmQgPSB3b3JkcywgDQogIGkgPSBzZXFfYWxvbmcod29yZCkNCikNCmRmICU+JSANCiAgZmlsdGVyKHN0cl9kZXRlY3Qod29yZCwgIngkIikpDQpgYGANCg0KVGFzay04OkNvdW50aW5nIHRoZSBvY2N1cnJlbmNlcyBvZiAiYSIgaW4gZWFjaCBlbGVtZW50IG9mIGEgY2hhcmFjdGVyIHZlY3Rvci4NCmBgYHtyfQ0KeCA8LSBjKCJhcHBsZSIsICJiYW5hbmEiLCAicGVhciIpDQpzdHJfY291bnQoeCwgImEiKQ0KYGBgDQpUYXNrLTk6IFNlZWluZyBhdmVyYWdlIG9mIGhvdyBtYW55IHZvd2VscyBwZXIgd29yZA0KYGBge3J9DQptZWFuKHN0cl9jb3VudCh3b3JkcywgIlthZWlvdV0iKSkNCmBgYA0KVGFzay0xMDogQWRkaW5nIGNvbHVtbnMgdG8gYSB0aWJibGUgdG8gY291bnQgdm93ZWxzIGFuZCBjb25zb25hbnRzIGluIGVhY2ggd29yZC4NCmBgYHtyfQ0KZGYgJT4lIA0KICBtdXRhdGUoDQogICAgdm93ZWxzID0gc3RyX2NvdW50KHdvcmQsICJbYWVpb3VdIiksDQogICAgY29uc29uYW50cyA9IHN0cl9jb3VudCh3b3JkLCAiW15hZWlvdV0iKQ0KICApDQpgYGANCg0KVGFzay0xMTpDb3VudGluZyAiYWJhIiBvY2N1cnJlbmNlcyBpbiAiYWJhYmFiYSIgYW5kIHNob3dpbmcgYWxsICJhYmEiIGluc3RhbmNlcy4NCmBgYHtyfQ0Kc3RyX2NvdW50KCJhYmFiYWJhIiwgImFiYSIpDQpzdHJfdmlld19hbGwoImFiYWJhYmEiLCAiYWJhIikNCmBgYA0KIyMgRXh0cmFjdCBtYXRjaGVzDQpUYXNrLTE6IERpc3BsYXlpbmcgdGhlIGxlbmd0aCBvZiBzZW50ZW5jZXMgYW5kIHNob3dpbmcgdGhlIGZpcnN0IGZldyBzZW50ZW5jZXMuDQpgYGB7cn0NCmxlbmd0aChzZW50ZW5jZXMpDQpoZWFkKHNlbnRlbmNlcykNCmBgYA0KDQpUYXNrLTI6IENyZWF0aW5nIGEgc3RyaW5nIHBhdHRlcm4gdG8gbWF0Y2ggY29sb3JzIGJ5IGNvbmNhdGVuYXRpbmcgdGhlbSB3aXRoIGEgcGlwZSBkZWxpbWl0ZXIuDQpgYGB7cn0NCmNvbG91cnMgPC0gYygicmVkIiwgIm9yYW5nZSIsICJ5ZWxsb3ciLCAiZ3JlZW4iLCAiYmx1ZSIsICJwdXJwbGUiKQ0KY29sb3VyX21hdGNoIDwtIHN0cl9jKGNvbG91cnMsIGNvbGxhcHNlID0gInwiKQ0KY29sb3VyX21hdGNoDQpgYGANClRhc2stMzogRmlsdGVyIHNlbnRlbmNlcyBmb3IgY29sb3JzIGFuZCBleHRyYWN0IG1hdGNoZXMsIHNob3dpbmcgdGhlIGZpcnN0IGZldy4NCmBgYHtyfQ0KaGFzX2NvbG91ciA8LSBzdHJfc3Vic2V0KHNlbnRlbmNlcywgY29sb3VyX21hdGNoKQ0KbWF0Y2hlcyA8LSBzdHJfZXh0cmFjdChoYXNfY29sb3VyLCBjb2xvdXJfbWF0Y2gpDQpoZWFkKG1hdGNoZXMpDQpgYGANClRhc2stNDpTaG93aW5nIGFsbCBzZW50ZW5jZXMgY29udGFpbmluZyBtdWx0aXBsZSBjb2xvcnMgYW5kIGhpZ2hsaWdodCB0aGUgbWF0Y2hlcy4NCmBgYHtyfQ0KbW9yZSA8LSBzZW50ZW5jZXNbc3RyX2NvdW50KHNlbnRlbmNlcywgY29sb3VyX21hdGNoKSA+IDFdDQpzdHJfdmlld19hbGwobW9yZSwgY29sb3VyX21hdGNoKQ0KYGBgDQpUYXNrLTU6RXh0cmFjdGluZyBhbGwgY29sb3IgbWF0Y2hlcyBmcm9tIHRoZSBzdWJzZXQgb2Ygc2VudGVuY2VzIGNvbnRhaW5pbmcgbXVsdGlwbGUgY29sb3JzLg0KYGBge3J9DQpzdHJfZXh0cmFjdChtb3JlLCBjb2xvdXJfbWF0Y2gpDQpgYGANCg0KVGFzay02OkV4dHJhY3RpbmcgYWxsIG9jY3VycmVuY2VzIG9mIGNvbG9ycyBmcm9tIHRoZSBzdWJzZXQgb2Ygc2VudGVuY2VzIGNvbnRhaW5pbmcgbXVsdGlwbGUgY29sb3JzLg0KYGBge3J9DQpzdHJfZXh0cmFjdF9hbGwobW9yZSwgY29sb3VyX21hdGNoKQ0KYGBgDQpUYXNrLTc6IEV4dHJhY3RpbmcgY29sb3JzIGZyb20gc2VudGVuY2VzIHdpdGggbXVsdGlwbGUgY29sb3JzIGFuZCBzaW1wbGlmeSwgYWxzbyBleHRyYWN0IGxvd2VyY2FzZSBsZXR0ZXJzIGZyb20gZWFjaCBlbGVtZW50IGluIHggYW5kIHNpbXBsaWZ5Lg0KYGBge3J9DQpzdHJfZXh0cmFjdF9hbGwobW9yZSwgY29sb3VyX21hdGNoLCBzaW1wbGlmeSA9IFRSVUUpDQp4IDwtIGMoImEiLCAiYSBiIiwgImEgYiBjIikNCnN0cl9leHRyYWN0X2FsbCh4LCAiW2Etel0iLCBzaW1wbGlmeSA9IFRSVUUpDQpgYGANCiMjIEdyb3VwZWQgbWF0Y2hlcyANClRhc2stMTogRXh0cmFjdGluZyBzZW50ZW5jZXMgY29udGFpbmluZyBub3VucyBkZWZpbmVkIGJ5IGEgcGF0dGVybiwgdGhlbiBleHRyYWN0cyB0aGUgbm91bnMgZnJvbSB0aG9zZSBzZW50ZW5jZXMuDQpgYGB7cn0NCm5vdW4gPC0gIihhfHRoZSkgKFteIF0rKSINCg0KaGFzX25vdW4gPC0gc2VudGVuY2VzICU+JQ0KICBzdHJfc3Vic2V0KG5vdW4pICU+JQ0KICBoZWFkKDEwKQ0KaGFzX25vdW4gJT4lIA0KICBzdHJfZXh0cmFjdChub3VuKQ0KYGBgDQoNClRhc2stMjoNCmBgYHtyfQ0KaGFzX25vdW4gJT4lIA0KICBzdHJfbWF0Y2gobm91bikNCmBgYA0KVGFzay0zOkNyZWF0aW5nIGEgdGliYmxlIHdpdGggY29sdW1ucyAnYXJ0aWNsZScgYW5kICdub3VuJyBleHRyYWN0ZWQgZnJvbSBzZW50ZW5jZXMgYmFzZWQgb24gYSBwYXR0ZXJuLg0KYGBge3J9DQp0aWJibGUoc2VudGVuY2UgPSBzZW50ZW5jZXMpICU+JSANCiAgdGlkeXI6OmV4dHJhY3QoDQogICAgc2VudGVuY2UsIGMoImFydGljbGUiLCAibm91biIpLCAiKGF8dGhlKSAoW14gXSspIiwgDQogICAgcmVtb3ZlID0gRkFMU0UNCiAgKQ0KYGBgDQojIyBSZXBsYWNpbmcgbWF0Y2hlcw0KVGFzay0xOiBSZXBsYWNpbmcgdGhlIGZpcnN0IHZvd2VsIGluIGVhY2ggd29yZCBvZiB4IHdpdGggYSBoeXBoZW4uDQogICAgICAgIFJlcGxhY2luZyBhbGwgdm93ZWxzIGluIGVhY2ggd29yZCBvZiB4IHdpdGggYSBoeXBoZW4uDQpgYGB7cn0NCnggPC0gYygiYXBwbGUiLCAicGVhciIsICJiYW5hbmEiKQ0Kc3RyX3JlcGxhY2UoeCwgIlthZWlvdV0iLCAiLSIpDQpzdHJfcmVwbGFjZV9hbGwoeCwgIlthZWlvdV0iLCAiLSIpDQpgYGANClRhc2stMjogUmVwbGFjaW5nIG51bWVyaWMgdmFsdWVzIGluIHggd2l0aCB0aGVpciBjb3JyZXNwb25kaW5nIHdvcmQgcmVwcmVzZW50YXRpb25zLg0KYGBge3J9DQp4IDwtIGMoIjEgaG91c2UiLCAiMiBjYXJzIiwgIjMgcGVvcGxlIikNCnN0cl9yZXBsYWNlX2FsbCh4LCBjKCIxIiA9ICJvbmUiLCAiMiIgPSAidHdvIiwgIjMiID0gInRocmVlIikpDQpgYGANClRhc2stMzpSZW9yZGVyaW5nIHdvcmRzIGluIHNlbnRlbmNlcyBieSBzd2FwcGluZyB0aGUgc2Vjb25kIGFuZCB0aGlyZCB3b3JkIHBvc2l0aW9ucy4NCmBgYHtyfQ0Kc2VudGVuY2VzICU+JSANCiAgc3RyX3JlcGxhY2UoIihbXiBdKykgKFteIF0rKSAoW14gXSspIiwgIlxcMSBcXDMgXFwyIikgJT4lIA0KICBoZWFkKDUpDQpgYGANCiMgU3BsaXR0aW5nDQpUYXNrLTE6IFNwbGl0dGluZyB0aGUgZmlyc3QgZml2ZSBzZW50ZW5jZXMgaW50byB3b3Jkcy4NCmBgYHtyfQ0Kc2VudGVuY2VzICU+JQ0KICBoZWFkKDUpICU+JSANCiAgc3RyX3NwbGl0KCIgIikNCmBgYA0KVGFzay0yOlNwbGl0dGluZyB0aGUgc3RyaW5nICdhfGJ8Y3xkJyBieSAnfCcgaW50byBhIHZlY3RvciBvZiBlbGVtZW50cy4NCmBgYHtyfQ0KImF8YnxjfGQiICU+JSANCiAgc3RyX3NwbGl0KCJcXHwiKSAlPiUgDQogIC5bWzFdXQ0KYGBgDQpUYXNrLTM6U3BsaXR0aW5nIHRoZSBmaXJzdCA1IHNlbnRlbmNlcyBieSBzcGFjZSBpbnRvIGEgbWF0cml4IG9mIHdvcmRzLg0KYGBge3J9DQpzZW50ZW5jZXMgJT4lDQogIGhlYWQoNSkgJT4lIA0KICBzdHJfc3BsaXQoIiAiLCBzaW1wbGlmeSA9IFRSVUUpDQpgYGANClRhc2stNDpTcGxpdHRpbmcgZWFjaCBmaWVsZCBzdHJpbmcgaW50byB0d28gcGFydHMgYXQgdGhlIGZpcnN0IG9jY3VycmVuY2Ugb2YgJzogJy4NCmBgYHtyfQ0KZmllbGRzIDwtIGMoIk5hbWU6IEhhZGxleSIsICJDb3VudHJ5OiBOWiIsICJBZ2U6IDM1IikNCmZpZWxkcyAlPiUgc3RyX3NwbGl0KCI6ICIsIG4gPSAyLCBzaW1wbGlmeSA9IFRSVUUpDQpgYGANClRhc2stNTogRGlzcGxheSB3b3JkIGJvdW5kYXJpZXMsIHNwbGl0IGJ5IHNwYWNlcywgYW5kIHNwbGl0IGJ5IHdvcmQgYm91bmRhcmllcywgcmVzcGVjdGl2ZWx5Lg0KYGBge3J9DQp4IDwtICJUaGlzIGlzIGEgc2VudGVuY2UuICBUaGlzIGlzIGFub3RoZXIgc2VudGVuY2UuIg0Kc3RyX3ZpZXdfYWxsKHgsIGJvdW5kYXJ5KCJ3b3JkIikpDQpzdHJfc3BsaXQoeCwgIiAiKVtbMV1dDQpzdHJfc3BsaXQoeCwgYm91bmRhcnkoIndvcmQiKSlbWzFdXQ0KYGBgDQojIE90aGVyIHR5cGVzIG9mIHBhdHRlcm4NCg0KVGFzay0xOiANCmBgYHtyfQ0KIyBUaGUgcmVndWxhciBjYWxsOg0Kc3RyX3ZpZXcoZnJ1aXQsICJuYW5hIikNCiMgSXMgc2hvcnRoYW5kIGZvcg0Kc3RyX3ZpZXcoZnJ1aXQsIHJlZ2V4KCJuYW5hIikpDQpgYGANClRhc2stMjpWaXN1YWxpemluZyBvY2N1cnJlbmNlcyBvZiAiYmFuYW5hIiBpbiBkaWZmZXJlbnQgY2FzZSB2YXJpYXRpb25zLg0KYGBge3J9DQpiYW5hbmFzIDwtIGMoImJhbmFuYSIsICJCYW5hbmEiLCAiQkFOQU5BIikNCnN0cl92aWV3KGJhbmFuYXMsICJiYW5hbmEiKQ0Kc3RyX3ZpZXcoYmFuYW5hcywgcmVnZXgoImJhbmFuYSIsIGlnbm9yZV9jYXNlID0gVFJVRSkpDQpgYGANClRhc2stMzogRXh0cmFjdGluZyBhbGwgbGluZXMgc3RhcnRpbmcgd2l0aCAiTGluZSIgZnJvbSB0aGUgdGV4dC4NCmBgYHtyfQ0KeCA8LSAiTGluZSAxXG5MaW5lIDJcbkxpbmUgMyINCnN0cl9leHRyYWN0X2FsbCh4LCAiXkxpbmUiKVtbMV1dDQpgYGANClRhc2stNDogRXh0cmFjdGluZyBhbGwgb2NjdXJyZW5jZXMgb2YgbGluZXMgc3RhcnRpbmcgd2l0aCAiTGluZSIgZnJvbSB0aGUgdGV4dCwgY29uc2lkZXJpbmcgZWFjaCBsaW5lIHNlcGFyYXRlbHkuDQpgYGB7cn0NCnN0cl9leHRyYWN0X2FsbCh4LCByZWdleCgiXkxpbmUiLCBtdWx0aWxpbmUgPSBUUlVFKSlbWzFdXQ0KYGBgDQpUYXNrLTU6Q3JlYXRpbmcgYSByZWd1bGFyIGV4cHJlc3Npb24gcGF0dGVybiBmb3IgcGhvbmUgbnVtYmVycywgYWxsb3dpbmcgZm9yIHZhcmlhdGlvbnMgaW4gZm9ybWF0dGluZywgYW5kIGF0dGVtcHRpbmcgdG8gbWF0Y2ggaXQgYWdhaW5zdCB0aGUgcHJvdmlkZWQgcGhvbmUgbnVtYmVyLg0KYGBge3J9DQpwaG9uZSA8LSByZWdleCgiDQogIFxcKD8gICAgICMgb3B0aW9uYWwgb3BlbmluZyBwYXJlbnMNCiAgKFxcZHszfSkgIyBhcmVhIGNvZGUNCiAgWykgLV0/ICAgIyBvcHRpb25hbCBjbG9zaW5nIHBhcmVucywgc3BhY2UsIG9yIGRhc2gNCiAgKFxcZHszfSkgIyBhbm90aGVyIHRocmVlIG51bWJlcnMNCiAgWyAtXT8gICAgIyBvcHRpb25hbCBzcGFjZSBvciBkYXNoDQogIChcXGR7M30pICMgdGhyZWUgbW9yZSBudW1iZXJzDQogICIsIGNvbW1lbnRzID0gVFJVRSkNCg0Kc3RyX21hdGNoKCI1MTQtNzkxLTgxNDEiLCBwaG9uZSkNCmBgYA0KVGFzay02Okluc3RhbGxsaW5nIHRoZSBwYWNrYWdlIGFuZCBCZW5jaG1hcmtpbmcgc3RyaW5nIGRldGVjdGlvbiBpbiAic2VudGVuY2VzIiB1c2luZyBmaXhlZCBhbmQgcmVnZXggcGF0dGVybnMgMjAgdGltZXMgZWFjaCwgY29tcGFyaW5nIHBlcmZvcm1hbmNlIHdpdGggbWljcm9iZW5jaG1hcmsuDQpgYGB7cn0NCg0KcGFja2FnZV90b19pbnN0YWxsIDwtIGMoIm1pY3JvYmVuY2htYXJrIikNCg0KZm9yIChwYWNrYWdlX25hbWUgaW4gcGFja2FnZV90b19pbnN0YWxsKSB7DQogIGlmICghcmVxdWlyZU5hbWVzcGFjZShwYWNrYWdlX25hbWUsIHF1aWV0bHkgPSBUUlVFKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMocGFja2FnZV9uYW1lKQ0KICB9DQp9DQpsaWJyYXJ5KG1pY3JvYmVuY2htYXJrKQ0KDQptaWNyb2JlbmNobWFyazo6bWljcm9iZW5jaG1hcmsoDQogIGZpeGVkID0gc3RyX2RldGVjdChzZW50ZW5jZXMsIGZpeGVkKCJ0aGUiKSksDQogIHJlZ2V4ID0gc3RyX2RldGVjdChzZW50ZW5jZXMsICJ0aGUiKSwNCiAgdGltZXMgPSAyMA0KICApDQpgYGANClRhc2stNzpTdGFydGluZyB3aXRoIGExIGJlaW5nICJcdTAwZTEiIGFuZCBhMiBiZWluZyAiYVx1MDMwMSIsIGJvdGggcmVwcmVzZW50aW5nIHRoZSBjaGFyYWN0ZXIgIsOhIiwgdGhleSBhcmUgY29tcGFyZWQgZm9yIGVxdWFsaXR5Lg0KYGBge3J9DQphMSA8LSAiXHUwMGUxIg0KYTIgPC0gImFcdTAzMDEiDQpjKGExLCBhMikNCmExID09IGEyDQpgYGANClRhc2stODogQ2hlY2tpbmcgaWYgYGExYCBjb250YWlucyB0aGUgZml4ZWQgc3RyaW5nIGBhMmAgcmV0dXJucyBgRkFMU0VgLCB3aGVyZWFzIHVzaW5nIGNvbGxhdGlvbiBydWxlcyByZXR1cm5zIGBUUlVFYC4NCmBgYHtyfQ0Kc3RyX2RldGVjdChhMSwgZml4ZWQoYTIpKQ0KDQpzdHJfZGV0ZWN0KGExLCBjb2xsKGEyKSkNCmBgYA0KVGFzay05OkNyZWF0aW5nIGEgdmVjdG9yIGBpYCB3aXRoIGRpZmZlcmVudCBmb3JtcyBvZiB0aGUgbGV0dGVyICJpIiwgdGhlbiB1c2luZyBgc3RyX3N1YnNldGAgdG8gZmlsdGVyIHRoZW0gYmFzZWQgb24gY29sbGF0aW9uLg0KYGBge3J9DQppIDwtIGMoIkkiLCAixLAiLCAiaSIsICLEsSIpDQppDQpzdHJfc3Vic2V0KGksIGNvbGwoImkiLCBpZ25vcmVfY2FzZSA9IFRSVUUpKQ0Kc3RyX3N1YnNldChpLCBjb2xsKCJpIiwgaWdub3JlX2Nhc2UgPSBUUlVFLCBsb2NhbGUgPSAidHIiKSkNCmBgYA0KDQpUYXNrLTEwOiBGZXRjaGluZyBsb2NhbGUgaW5mb3JtYXRpb24uDQpgYGB7cn0NCnN0cmluZ2k6OnN0cmlfbG9jYWxlX2luZm8oKQ0KDQpgYGANClRhc2stMTE6VmlzdWFsaXppbmcgd29yZCBib3VuZGFyaWVzIGFuZCBleHRyYWN0cyBhbGwgd29yZHMgZnJvbSB0aGUgc3RyaW5nLg0KYGBge3J9DQp4IDwtICJUaGlzIGlzIGEgc2VudGVuY2UuIg0Kc3RyX3ZpZXdfYWxsKHgsIGJvdW5kYXJ5KCJ3b3JkIikpDQpzdHJfZXh0cmFjdF9hbGwoeCwgYm91bmRhcnkoIndvcmQiKSkNCmBgYA0KIyBDSC0xNTogRmFjdG9ycw0KIyMgQ3JlYXRpZyBmYWN0b3JzDQpUYXNrLTE6QWRkaW5nIGNoYXJhY3RlciB2ZWN0b3IgaW4gdmFyaWFibGUgeDENCmBgYHtyfQ0KeDEgPC0gYygiRGVjIiwgIkFwciIsICJKYW4iLCAiTWFyIikNCmBgYA0KDQpUYXNrLTI6QWRkaW5nIGNoYXJhY3RlciB2ZWN0b3IgaW4gdmFyaWFibGUgeDINCmBgYHtyfQ0KeDIgPC0gYygiRGVjIiwgIkFwciIsICJKYW0iLCAiTWFyIikNCmBgYA0KDQpUYXNrLTM6U29ydGluZyBYMSANCmBgYHtyfQ0Kc29ydCh4MSkNCg0KYGBgDQpUYXNrLTQ6QWRkaW5nIENoYXJhY3RlciB2ZWN0b3IgaW4gbW9udGhfbGV2ZWxzDQpgYGB7cn0NCm1vbnRoX2xldmVscyA8LSBjKA0KICAiSmFuIiwgIkZlYiIsICJNYXIiLCAiQXByIiwgIk1heSIsICJKdW4iLCANCiAgIkp1bCIsICJBdWciLCAiU2VwIiwgIk9jdCIsICJOb3YiLCAiRGVjIg0KKQ0KYGBgDQoNClRhc2stNTpBc3NpZ25pbmcgdGhlIGZhY3RvciBsZXZlbHMgdG8gdGhlIHZhcmlhYmxlIHgxLCB1c2luZyB0aGUgcHJlZGVmaW5lZCBtb250aF9sZXZlbHMuDQpgYGB7cn0NCnkxIDwtIGZhY3Rvcih4MSwgbGV2ZWxzID0gbW9udGhfbGV2ZWxzKQ0KeQ0KYGBgDQpUYXNrLTY6U29ydGluZyB0aGUgZmFjdG9yIGxldmVscyBpbiB5MS4NCmBgYHtyfQ0Kc29ydCh5MSkNCmBgYA0KVGFzay03OmNyZWF0aW5nIGEgZmFjdG9yIHkyIGZyb20geDIgd2l0aCBjdXN0b20gbGV2ZWxzIHNwZWNpZmllZCBieSBtb250aF9sZXZlbHMuDQpgYGB7cn0NCnkyIDwtIGZhY3Rvcih4MiwgbGV2ZWxzID0gbW9udGhfbGV2ZWxzKQ0KeTINCmBgYA0KVGFzay04OnBhcnNpbmcgdGhlIHZhbHVlcyBpbiB4MiBhcyBmYWN0b3JzDQpgYGB7cn0NCnkyIDwtIHBhcnNlX2ZhY3Rvcih4MiwgbGV2ZWxzID0gbW9udGhfbGV2ZWxzKQ0KYGBgDQpUYXNrLTk6IG9taXR0aW5nIHRoZSBsZXZlbHMuDQpgYGB7cn0NCmZhY3Rvcih4MSkNCmBgYA0KVGFzay0xMDpDcmVhdGluZyBhIGZhY3RvciBmMSBmcm9tIHRoZSB2YWx1ZXMgaW4geDEsIHVzaW5nIHRoZSB1bmlxdWUgdmFsdWVzIG9mIHgxIGFzIGxldmVscy4NCmBgYHtyfQ0KZjEgPC0gZmFjdG9yKHgxLCBsZXZlbHMgPSB1bmlxdWUoeDEpKQ0KZjENCmBgYA0KVGFzay0xMTogY3JlYXRpbmcgYSBmYWN0b3IgZjIgZnJvbSB0aGUgdmFsdWVzIGluIHgxLCBvcmRlcmluZyB0aGVtIGFjY29yZGluZyB0byB0aGVpciBhcHBlYXJhbmNlIGluIHgxLg0KYGBge3J9DQpmMiA8LSB4MSAlPiUgZmFjdG9yKCkgJT4lIGZjdF9pbm9yZGVyKCkNCmYyDQpgYGANClRhc2stMTI6T21pdHRpbmcgbGV2ZWxzMg0KYGBge3J9DQpsZXZlbHMoZjIpDQpgYGANCiMgR2VuZXJhbCBTb2NpYWwgU3VydmV5DQpUYXNrLTE6TG9hZGluZyBkYXRhc2V0cw0KYGBge3J9DQpnc3NfY2F0DQpgYGANCg0KVGFzay0yOlNlZWluZyBsZXZlbHMgdGhyb3VnaCBjb3VudCgpDQpgYGB7cn0NCmdzc19jYXQgJT4lDQogIGNvdW50KHJhY2UpDQpgYGANClRhc2stMzpBbHNvIHNlZWluZyB0aHJvdWdoIGJhcigpDQpgYGB7cn0NCmdncGxvdChnc3NfY2F0LCBhZXMocmFjZSkpICsNCiAgZ2VvbV9iYXIoKQ0KYGBgDQoNClRhc2stNDpHZW5lcmF0aW5nIGEgYmFyIHBsb3QgdXNpbmcgZ2dwbG90KCkNCmBgYHtyfQ0KZ2dwbG90KGdzc19jYXQsYWVzKHJhY2UpKStnZW9tX2JhcigpK3NjYWxlX3hfZGlzY3JldGUoZHJvcD1GQUxTRSkNCmBgYA0KIyBNb2RpZnlpbmcgZmFjdG9yIG9yZGVyDQpUYXNrLTE6Y2FsY3VsYXRpbmcgc3VtbWFyeSBzdGF0aXN0aWNzIGFuZCB0aGVuIGNyZWF0aW5nIHNjYXR0ZXIgcGxvdCANCmBgYHtyfQ0KcmVsaWdfc3VtbWFyeSA8LSBnc3NfY2F0ICU+JQ0KICBncm91cF9ieShyZWxpZykgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBhZ2UgPSBtZWFuKGFnZSwgbmEucm0gPSBUUlVFKSwNCiAgICB0dmhvdXJzID0gbWVhbih0dmhvdXJzLCBuYS5ybSA9IFRSVUUpLA0KICAgIG4gPSBuKCkNCiAgKQ0KDQpnZ3Bsb3QocmVsaWdfc3VtbWFyeSwgYWVzKHR2aG91cnMsIHJlbGlnKSkgKyBnZW9tX3BvaW50KCkNCmBgYA0KDQpUYXNrLTI6R2VuZXJhdGluZyBhIHNjYXR0ZXIgcGxvdCB1c2luZyBgZ2dwbG90YCwgd2hlcmUgdGhlIHgtYXhpcyByZXByZXNlbnRzIHRoZSBtZWFuIFRWIGhvdXJzIChgdHZob3Vyc2ApLCBhbmQgdGhlIHktYXhpcyByZXByZXNlbnRzIHRoZSBgcmVsaWdgIHZhcmlhYmxlIHJlb3JkZXJlZCBieSBtZWFuIFRWIGhvdXJzLg0KYGBge3J9DQpnZ3Bsb3QocmVsaWdfc3VtbWFyeSwgYWVzKHR2aG91cnMsIGZjdF9yZW9yZGVyKHJlbGlnLCB0dmhvdXJzKSkpICsNCiAgZ2VvbV9wb2ludCgpDQpgYGANClRhc2stMzpDcmVhdGluZyBhIHNjYXR0ZXIgcGxvdCB1c2luZyBnZ3Bsb3QuDQpgYGB7cn0NCnJlbGlnX3N1bW1hcnkgJT4lDQogIG11dGF0ZShyZWxpZyA9IGZjdF9yZW9yZGVyKHJlbGlnLCB0dmhvdXJzKSkgJT4lDQogIGdncGxvdChhZXModHZob3VycywgcmVsaWcpKSArDQogICAgZ2VvbV9wb2ludCgpDQpgYGANClRhc2stNDpHZW5lcmF0aW5nIGEgc2NhdHRlciBwbG90IHVzaW5nIGdncGxvdA0KYGBge3J9DQpyaW5jb21lX3N1bW1hcnkgPC0gZ3NzX2NhdCAlPiUNCiAgZ3JvdXBfYnkocmluY29tZSkgJT4lDQogIHN1bW1hcmlzZSgNCiAgICBhZ2UgPSBtZWFuKGFnZSwgbmEucm0gPSBUUlVFKSwNCiAgICB0dmhvdXJzID0gbWVhbih0dmhvdXJzLCBuYS5ybSA9IFRSVUUpLA0KICAgIG4gPSBuKCkNCiAgKQ0KDQpnZ3Bsb3QocmluY29tZV9zdW1tYXJ5LCBhZXMoYWdlLCBmY3RfcmVvcmRlcihyaW5jb21lLCBhZ2UpKSkgKyBnZW9tX3BvaW50KCkNCmBgYA0KVGFzay01OiBjcmVhdGVzIGEgc2NhdHRlciBwbG90IG9mIHRoZSBhdmVyYWdlIGFnZSBieSBpbmNvbWUgbGV2ZWwsIHdpdGggIk5vdCBhcHBsaWNhYmxlIiBhcyB0aGUgcmVmZXJlbmNlIGxldmVsIGZvciBpbmNvbWUNCmBgYHtyfQ0KZ2dwbG90KHJpbmNvbWVfc3VtbWFyeSwgYWVzKGFnZSwgZmN0X3JlbGV2ZWwocmluY29tZSwgIk5vdCBhcHBsaWNhYmxlIikpKSArDQogIGdlb21fcG9pbnQoKQ0KYGBgDQpUYXNrLTY6Y2FsY3VsYXRpbmcgdGhlIHByb3BvcnRpb24gb2YgZWFjaCBtYXJpdGFsIHN0YXR1cyBncm91cCBhY3Jvc3MgZGlmZmVyZW50IGFnZSBncm91cHMgYW5kIGNyZWF0ZXMgYSBsaW5lIHBsb3Qgc2hvd2luZyB0aGUgZGlzdHJpYnV0aW9uIG9mIG1hcml0YWwgc3RhdHVzIHByb3BvcnRpb25zIGJ5IGFnZS4NCmBgYHtyfQ0KYnlfYWdlIDwtIGdzc19jYXQgJT4lDQogIGZpbHRlcighaXMubmEoYWdlKSkgJT4lDQogIGNvdW50KGFnZSwgbWFyaXRhbCkgJT4lDQogIGdyb3VwX2J5KGFnZSkgJT4lDQogIG11dGF0ZShwcm9wID0gbiAvIHN1bShuKSkNCg0KZ2dwbG90KGJ5X2FnZSwgYWVzKGFnZSwgcHJvcCwgY29sb3VyID0gbWFyaXRhbCkpICsNCiAgZ2VvbV9saW5lKG5hLnJtID0gVFJVRSkNCg0KZ2dwbG90KGJ5X2FnZSwgYWVzKGFnZSwgcHJvcCwgY29sb3VyID0gZmN0X3Jlb3JkZXIyKG1hcml0YWwsIGFnZSwgcHJvcCkpKSArDQogIGdlb21fbGluZSgpICsNCiAgbGFicyhjb2xvdXIgPSAibWFyaXRhbCIpDQpgYGANClRhc2stNzogQWRqdXN0aW5nIHRoZSBvcmRlciBvZiB0aGUgIm1hcml0YWwiIHZhcmlhYmxlIGJhc2VkIG9uIGZyZXF1ZW5jeSBhbmQgdGhlbiByZXZlcnNlcyB0aGUgb3JkZXIgYmVmb3JlIGdlbmVyYXRpbmcgYSBiYXIgcGxvdCBpbGx1c3RyYXRpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiBtYXJpdGFsIHN0YXR1cy4NCmBgYHtyfQ0KZ3NzX2NhdCAlPiUNCiAgbXV0YXRlKG1hcml0YWwgPSBtYXJpdGFsICU+JSBmY3RfaW5mcmVxKCkgJT4lIGZjdF9yZXYoKSkgJT4lDQogIGdncGxvdChhZXMobWFyaXRhbCkpICsNCiAgICBnZW9tX2JhcigpDQpgYGANCiMgTW9kaWZ5aW5nIGZhY3RvciBsZXZlbHMNCg0KVGFzay0xOiBjb3VudGluZyB0aGUgZnJlcXVlbmN5IG9mIGVhY2ggdW5pcXVlIHZhbHVlIGluIHRoZSAicGFydHlpZCIgdmFyaWFibGUgb2YgdGhlICJnc3NfY2F0IiBkYXRhc2V0Lg0KYGBge3J9DQpnc3NfY2F0JT4lY291bnQocGFydHlpZCkNCmBgYA0KVGFzay0yOlJlY29yZGluZyB0aGUgbGV2ZWxzIG9mIHRoZSAicGFydHlpZCIgdmFyaWFibGUgaW4gdGhlICJnc3NfY2F0IiBkYXRhc2V0IGFuZCB0aGVuIGNvdW50cyB0aGUgZnJlcXVlbmN5IG9mIGVhY2ggdW5pcXVlIHJlY29yZGVkIHZhbHVlLg0KYGBge3J9DQpnc3NfY2F0ICU+JQ0KICBtdXRhdGUoIHBhcnR5aWQ9ZmN0X3JlY29kZShwYXJ0eWlkLA0KICAgICJSZXB1YmxpY2FuLCBzdHJvbmciICAgID0gIlN0cm9uZyByZXB1YmxpY2FuIiwNCiAgICAiUmVwdWJsaWNhbiwgd2VhayIgICAgICA9ICJOb3Qgc3RyIHJlcHVibGljYW4iLA0KICAgICJJbmRlcGVuZGVudCwgbmVhciByZXAiID0gIkluZCxuZWFyIHJlcCIsDQogICAgIkluZGVwZW5kZW50LCBuZWFyIGRlbSIgPSAiSW5kLG5lYXIgZGVtIiwNCiAgICAiRGVtb2NyYXQsIHdlYWsiICAgICAgICA9ICJOb3Qgc3RyIGRlbW9jcmF0IiwNCiAgICAiRGVtb2NyYXQsIHN0cm9uZyIgICAgICA9ICJTdHJvbmcgZGVtb2NyYXQiDQogICAgKSklPiUNCiAgY291bnQocGFydHlpZCkNCmBgYA0KVGFzay0zOlJlY2F0ZWdvcml6aW5nIGFuZCBjb3VudGluZyBwYXJ0eSBhZmZpbGlhdGlvbnMgaW4gdGhlICJnc3NfY2F0IiBkYXRhc2V0Lg0KYGBge3J9DQpnc3NfY2F0ICU+JQ0KICBtdXRhdGUocGFydHlpZCA9IGZjdF9yZWNvZGUocGFydHlpZCwNCiAgICAiUmVwdWJsaWNhbiwgc3Ryb25nIiAgICA9ICJTdHJvbmcgcmVwdWJsaWNhbiIsDQogICAgIlJlcHVibGljYW4sIHdlYWsiICAgICAgPSAiTm90IHN0ciByZXB1YmxpY2FuIiwNCiAgICAiSW5kZXBlbmRlbnQsIG5lYXIgcmVwIiA9ICJJbmQsbmVhciByZXAiLA0KICAgICJJbmRlcGVuZGVudCwgbmVhciBkZW0iID0gIkluZCxuZWFyIGRlbSIsDQogICAgIkRlbW9jcmF0LCB3ZWFrIiAgICAgICAgPSAiTm90IHN0ciBkZW1vY3JhdCIsDQogICAgIkRlbW9jcmF0LCBzdHJvbmciICAgICAgPSAiU3Ryb25nIGRlbW9jcmF0IiwNCiAgICAiT3RoZXIiICAgICAgICAgICAgICAgICA9ICJObyBhbnN3ZXIiLA0KICAgICJPdGhlciIgICAgICAgICAgICAgICAgID0gIkRvbid0IGtub3ciLA0KICAgICJPdGhlciIgICAgICAgICAgICAgICAgID0gIk90aGVyIHBhcnR5Ig0KICApKSAlPiUNCiAgY291bnQocGFydHlpZCkNCmBgYA0KVGFzay00OiBDb2xsYXBzaW5nIGNhdGVnb3JpZXMgd2l0aGluIHRoZSAicGFydHlpZCIgdmFyaWFibGUgaW4gdGhlICJnc3NfY2F0IiBkYXRhc2V0IGludG8gYnJvYWRlciBncm91cHMgYW5kIHRoZW4gY291bnRpbmcgdGhlIGZyZXF1ZW5jeSBvZiBlYWNoIGNvbGxhcHNlZCBjYXRlZ29yeS4NCmBgYHtyfQ0KZ3NzX2NhdCU+JQ0KICBtdXRhdGUocGFydHlpZD1mY3RfY29sbGFwc2UocGFydHlpZCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG90aGVyPWMoIk5vIGFuc3dlciIsICJEb24ndCBrbm93IiwgIk90aGVyIHBhcnR5IiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXA9YygiU3Ryb25nIHJlcHVibGljYW4iLCAiTm90IHN0ciByZXB1YmxpY2FuIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpbmQ9YygiSW5kLG5lYXIgcmVwIiwgIkluZGVwZW5kZW50IiwgIkluZCxuZWFyIGRlbSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVtPWMoIk5vdCBzdHIgZGVtb2NyYXQiLCAiU3Ryb25nIGRlbW9jcmF0IikpKSAlPiUNCiAgY291bnQocGFydHlpZCkNCmBgYA0KDQpUYXNrLTU6Q291bnRpbmcgYW5kIGFnZ3JlZ2F0aW5nIHJlbGlnaW91cyBhZmZpbGlhdGlvbnMgaW4gdGhlICJnc3NfY2F0IiBkYXRhc2V0IGFmdGVyIGx1bXBpbmcgdG9nZXRoZXIgbGVzcyBmcmVxdWVudCBjYXRlZ29yaWVzLg0KYGBge3J9DQpnc3NfY2F0ICU+JQ0KICBtdXRhdGUocmVsaWcgPSBmY3RfbHVtcChyZWxpZykpICU+JQ0KICBjb3VudChyZWxpZykNCmBgYA0KVGFzay02OiJTdW1tYXJpemluZyByZWxpZ2lvdXMgYWZmaWxpYXRpb25zIGFmdGVyIGx1bXBpbmcgaW5mcmVxdWVudCBjYXRlZ29yaWVzIGFuZCBzb3J0LiINCmBgYHtyfQ0KZ3NzX2NhdCAlPiUNCiAgbXV0YXRlKHJlbGlnID0gZmN0X2x1bXAocmVsaWcsIG4gPSAxMCkpICU+JQ0KICBjb3VudChyZWxpZywgc29ydCA9IFRSVUUpICU+JQ0KICBwcmludChuID0gSW5mKQ0KYGBgDQoNCiMgQ0gtRGF0YSBhbmQgVGltZXMNCg0KVGFzay0xOkxvYWRpbmcgbGlicmFyeQ0KYGBge3J9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCg0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KG55Y2ZsaWdodHMxMykNCmBgYA0KDQojIyBDcmVhdGluZyBkYXRlcy90aW1lcw0KVGFzay0xOiBQcmludGluZyAgY3VycmVudCBkYXRlIG9yIGRhdGUtdGltZQ0KYGBge3J9DQp0b2RheSgpDQpub3coKQ0KYGBgDQojIyBGb3JtIHN0cmluZ3MNClRhc2stMjpjb252ZXJ0aW5nIGRhdGUgc3RyaW5ncyB0byBkYXRlIG9iamVjdHMgaW4gZGlmZmVyZW50IGZvcm1hdHMuDQpgYGB7cn0NCnltZCgiMjAxNy0wMS0zMSIpDQptZHkoIkphbnVhcnkgMzFzdCwgMjAxNyIpDQpkbXkoIjMxLUphbi0yMDE3IikNCmBgYA0KYGBge3J9DQp5bWQoMjAxNzAxMzEpDQpgYGANCg0KYGBge3J9DQp5bWRfaG1zKCIyMDE3LTAxLTMxIDIwOjExOjU5IikNCm1keV9obSgiMDEvMzEvMjAxNyAwODowMSIpDQpgYGANCg0KDQpgYGB7cn0NCmZsaWdodHMgJT4lIA0KICBzZWxlY3QoeWVhciwgbW9udGgsIGRheSwgaG91ciwgbWludXRlKQ0KZmxpZ2h0cyAlPiUgDQogIHNlbGVjdCh5ZWFyLCBtb250aCwgZGF5LCBob3VyLCBtaW51dGUpICU+JSANCiAgbXV0YXRlKGRlcGFydHVyZSA9IG1ha2VfZGF0ZXRpbWUoeWVhciwgbW9udGgsIGRheSwgaG91ciwgbWludXRlKSkNCmBgYA0KVGFzazogQ3JlYXRpbmcgZGF0ZS10aW1lIG9iamVjdHMgZnJvbSBob3VyLW1pbnV0ZSB0aW1lIGRhdGEgaW4gdGhlICdmbGlnaHRzJyBkYXRhc2V0IGFuZCBmaWx0ZXJpbmcgb3V0IHJvd3Mgd2l0aCBtaXNzaW5nIGRlcGFydHVyZSBvciBhcnJpdmFsIHRpbWVzDQpgYGB7cn0NCm1ha2VfZGF0ZXRpbWVfMTAwIDwtIGZ1bmN0aW9uKHllYXIsIG1vbnRoLCBkYXksIHRpbWUpIHsNCiAgbWFrZV9kYXRldGltZSh5ZWFyLCBtb250aCwgZGF5LCB0aW1lICUvJSAxMDAsIHRpbWUgJSUgMTAwKQ0KfQ0KDQpmbGlnaHRzX2R0IDwtIGZsaWdodHMgJT4lIA0KICBmaWx0ZXIoIWlzLm5hKGRlcF90aW1lKSwgIWlzLm5hKGFycl90aW1lKSkgJT4lIA0KICBtdXRhdGUoDQogICAgZGVwX3RpbWUgPSBtYWtlX2RhdGV0aW1lXzEwMCh5ZWFyLCBtb250aCwgZGF5LCBkZXBfdGltZSksDQogICAgYXJyX3RpbWUgPSBtYWtlX2RhdGV0aW1lXzEwMCh5ZWFyLCBtb250aCwgZGF5LCBhcnJfdGltZSksDQogICAgc2NoZWRfZGVwX3RpbWUgPSBtYWtlX2RhdGV0aW1lXzEwMCh5ZWFyLCBtb250aCwgZGF5LCBzY2hlZF9kZXBfdGltZSksDQogICAgc2NoZWRfYXJyX3RpbWUgPSBtYWtlX2RhdGV0aW1lXzEwMCh5ZWFyLCBtb250aCwgZGF5LCBzY2hlZF9hcnJfdGltZSkNCiAgKSAlPiUgDQogIHNlbGVjdChvcmlnaW4sIGRlc3QsIGVuZHNfd2l0aCgiZGVsYXkiKSwgZW5kc193aXRoKCJ0aW1lIikpDQoNCmZsaWdodHNfZHQNCmBgYA0KVGFzazogUGxvdHRpbmcgdGhlIGZyZXF1ZW5jeSBvZiBmbGlnaHRzIG92ZXIgdGltZSB1c2luZyBkZXBhcnR1cmUgZGF0ZS10aW1lDQoNCmBgYHtyfQ0KZmxpZ2h0c19kdCAlPiUgDQogIGdncGxvdChhZXMoZGVwX3RpbWUpKSArIA0KICBnZW9tX2ZyZXFwb2x5KGJpbndpZHRoID0gODY0MDApIA0KYGBgDQpUYXNrOiBQbG90dGluZyB0aGUgZnJlcXVlbmN5IG9mIGZsaWdodHMgb3ZlciB0aW1lIGZvciBhIHNwZWNpZmljIHBlcmlvZCB1c2luZyBkZXBhcnR1cmUgZGF0ZS10aW1lDQoNCmBgYHtyfQ0KZmxpZ2h0c19kdCAlPiUgDQogIGZpbHRlcihkZXBfdGltZSA8IHltZCgyMDEzMDEwMikpICU+JSANCiAgZ2dwbG90KGFlcyhkZXBfdGltZSkpICsgDQogIGdlb21fZnJlcXBvbHkoYmlud2lkdGggPSA2MDApICMgNjAwIHMgPSAxMCBtaW51dGVzDQpgYGANClRhc2s6IHRvIGNvbnZlcnQgdG9kYXkncyBkYXRlIHRvIGRhdGUtdGltZSBvYmplY3QNCmBgYHtyfQ0KYXNfZGF0ZXRpbWUodG9kYXkoKSkNCg0KYXNfZGF0ZShub3coKSkNCg0KYXNfZGF0ZSgzNjUgKiAxMCArIDIpDQoNCmBgYA0KRGF0ZS10aW1lIGNvbXBvbmVudHMNClRhc2s6IEV4dHJhY3RpbmcgdmFyaW91cyBjb21wb25lbnRzIG9mIGEgZGF0ZS10aW1lIG9iamVjdA0KYGBge3J9DQpkYXRldGltZSA8LSB5bWRfaG1zKCIyMDE2LTA3LTA4IDEyOjM0OjU2IikNCnllYXIoZGF0ZXRpbWUpDQptb250aChkYXRldGltZSkNCm1kYXkoZGF0ZXRpbWUpDQp5ZGF5KGRhdGV0aW1lKQ0Kd2RheShkYXRldGltZSkNCm1vbnRoKGRhdGV0aW1lLCBsYWJlbCA9IFRSVUUpDQp3ZGF5KGRhdGV0aW1lLCBsYWJlbCA9IFRSVUUsIGFiYnIgPSBGQUxTRSkNCmBgYA0KVGFzazogUGxvdHRpbmcgdGhlIGZyZXF1ZW5jeSBvZiBmbGlnaHRzIGJ5IGRheSBvZiB0aGUgd2Vlaw0KYGBge3J9DQpmbGlnaHRzX2R0ICU+JSANCiAgbXV0YXRlKHdkYXkgPSB3ZGF5KGRlcF90aW1lLCBsYWJlbCA9IFRSVUUpKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IHdkYXkpKSArDQogICAgZ2VvbV9iYXIoKQ0KYGBgDQpUYXNrOiBQbG90dGluZyBhdmVyYWdlIGRlbGF5IGJ5IG1pbnV0ZSBvZiBkZXBhcnR1cmUgdGltZQ0KYGBge3J9DQpmbGlnaHRzX2R0ICU+JSANCiAgbXV0YXRlKG1pbnV0ZSA9IG1pbnV0ZShkZXBfdGltZSkpICU+JSANCiAgZ3JvdXBfYnkobWludXRlKSAlPiUgDQogIHN1bW1hcmlzZSgNCiAgICBhdmdfZGVsYXkgPSBtZWFuKGFycl9kZWxheSwgbmEucm0gPSBUUlVFKSwNCiAgICBuID0gbigpKSAlPiUgDQogIGdncGxvdChhZXMobWludXRlLCBhdmdfZGVsYXkpKSArDQogICAgZ2VvbV9saW5lKCkNCmBgYA0KVGFzazogUGxvdHRpbmcgYXZlcmFnZSBkZWxheSBieSBtaW51dGUgb2Ygc2NoZWR1bGVkIGRlcGFydHVyZSB0aW1lDQoNCmBgYHtyfQ0Kc2NoZWRfZGVwIDwtIGZsaWdodHNfZHQgJT4lIA0KICBtdXRhdGUobWludXRlID0gbWludXRlKHNjaGVkX2RlcF90aW1lKSkgJT4lIA0KICBncm91cF9ieShtaW51dGUpICU+JSANCiAgc3VtbWFyaXNlKA0KICAgIGF2Z19kZWxheSA9IG1lYW4oYXJyX2RlbGF5LCBuYS5ybSA9IFRSVUUpLA0KICAgIG4gPSBuKCkpDQoNCmdncGxvdChzY2hlZF9kZXAsIGFlcyhtaW51dGUsIGF2Z19kZWxheSkpICsNCiAgZ2VvbV9saW5lKCkNCmBgYA0KVGFzazogUGxvdHRpbmcgdGhlIG51bWJlciBvZiBmbGlnaHRzIGJ5IG1pbnV0ZSBvZiBzY2hlZHVsZWQgZGVwYXJ0dXJlIHRpbWUNCmBgYHtyfQ0KZ2dwbG90KHNjaGVkX2RlcCwgYWVzKG1pbnV0ZSwgbikpICsNCiAgZ2VvbV9saW5lKCkNCmBgYA0KUm91bmRpbmcNClRhc2s6UGxvdHRpbmcgdGhlIG51bWJlciBvZiBmbGlnaHRzIGJ5IHdlZWssIHJvdW5kaW5nIHRvIHRoZSBuZWFyZXN0IHdlZWsNCg0KYGBge3J9DQpmbGlnaHRzX2R0ICU+JSANCiAgY291bnQod2VlayA9IGZsb29yX2RhdGUoZGVwX3RpbWUsICJ3ZWVrIikpICU+JSANCiAgZ2dwbG90KGFlcyh3ZWVrLCBuKSkgKw0KICAgIGdlb21fbGluZSgpDQoNCmBgYA0Kc2V0dGluZyBjb21wb3VuZHMNClRhc2s6IFNldHRpbmcgdXAgYSBkYXRlLXRpbWUgb2JqZWN0DQpgYGB7cn0NCihkYXRldGltZSA8LSB5bWRfaG1zKCIyMDE2LTA3LTA4IDEyOjM0OjU2IikpDQp5ZWFyKGRhdGV0aW1lKSA8LSAyMDIwDQpkYXRldGltZQ0KbW9udGgoZGF0ZXRpbWUpIDwtIDAxDQpkYXRldGltZQ0KaG91cihkYXRldGltZSkgPC0gaG91cihkYXRldGltZSkgKyAxDQpkYXRldGltZQ0KdXBkYXRlKGRhdGV0aW1lLCB5ZWFyID0gMjAyMCwgbW9udGggPSAyLCBtZGF5ID0gMiwgaG91ciA9IDIpDQpgYGANCmBgYHtyfQ0KeW1kKCIyMDE1LTAyLTAxIikgJT4lIA0KICB1cGRhdGUobWRheSA9IDMwKQ0KeW1kKCIyMDE1LTAyLTAxIikgJT4lIA0KICB1cGRhdGUoaG91ciA9IDQwMCkNCmBgYA0KVGFzazogQ3JlYXRpbmcgYSBuZXcgdmFyaWFibGUgJ2RlcF9ob3VyJyBieSB1cGRhdGluZyB0aGUgJ2RlcF90aW1lJyB0byB0aGUgZmlyc3QgZGF5IG9mIHRoZSB5ZWFyDQoNCmBgYHtyfQ0KZmxpZ2h0c19kdCAlPiUgDQogIG11dGF0ZShkZXBfaG91ciA9IHVwZGF0ZShkZXBfdGltZSwgeWRheSA9IDEpKSAlPiUgDQogIGdncGxvdChhZXMoZGVwX2hvdXIpKSArDQogICAgZ2VvbV9mcmVxcG9seShiaW53aWR0aCA9IDMwMCkNCmBgYA0KVGltZSBTcGFucw0KQ29tcHV0ZSB0aGUgYWdlIG9mIGEgcGVyc29uIGJhc2VkIG9uIHRoZWlyIGJpcnRoZGF0ZSBhbmQgdG9kYXkncyBkYXRlDQpgYGB7cn0NCmhfYWdlIDwtIHRvZGF5KCkgLSB5bWQoMTk3OTEwMTQpDQpoX2FnZQ0KYXMuZHVyYXRpb24oaF9hZ2UpDQpgYGANCmBgYHtyfQ0KZHNlY29uZHMoMTUpDQpkbWludXRlcygxMCkNCmRob3VycyhjKDEyLCAyNCkpDQpkZGF5cygwOjUpDQpkd2Vla3MoMykNCmR5ZWFycygxKQ0KYGBgDQpgYGB7cn0NCjIgKiBkeWVhcnMoMSkNCmR5ZWFycygxKSArIGR3ZWVrcygxMikgKyBkaG91cnMoMTUpDQp0b21vcnJvdyA8LSB0b2RheSgpICsgZGRheXMoMSkNCmxhc3RfeWVhciA8LSB0b2RheSgpIC0gZHllYXJzKDEpDQpvbmVfcG0gPC0geW1kX2htcygiMjAxNi0wMy0xMiAxMzowMDowMCIsIHR6ID0gIkFtZXJpY2EvTmV3X1lvcmsiKQ0Kb25lX3BtDQpvbmVfcG0gKyBkZGF5cygxKQ0KYGBgDQpQZXJpb2RzDQpDcmVhdGUgcGVyaW9kIG9iamVjdHMgcmVwcmVzZW50aW5nIGRpZmZlcmVudCB0aW1lIHNwYW5zIGFuZCBQZXJmb3JtIGFyaXRobWV0aWMgb3BlcmF0aW9ucyB3aXRoIHBlcmlvZCBvYmplY3RzDQpgYGB7cn0NCm9uZV9wbQ0Kb25lX29tID0gZGF5cygxKQ0KYGBgDQpgYGB7cn0NCnNlY29uZHMoMTUpDQptaW51dGVzKDEwKQ0KaG91cnMoYygxMiwgMjQpKQ0KZGF5cyg3KQ0KbW9udGhzKDE6NikNCndlZWtzKDMpDQp5ZWFycygxKQ0KYGBgDQoNCmBgYHtyfQ0KMTAgKiAobW9udGhzKDYpICsgZGF5cygxKSkNCmRheXMoNTApICsgaG91cnMoMjUpICsgbWludXRlcygyKQ0KYGBgDQoNCmBgYHtyfQ0KeW1kKCIyMDE2LTAxLTAxIikgKyBkeWVhcnMoMSkNCnltZCgiMjAxNi0wMS0wMSIpICsgeWVhcnMoMSkNCm9uZV9wbSArIGRkYXlzKDEpDQpvbmVfcG0gKyBkYXlzKDEpDQpgYGANCg0KRmlsdGVyIGZsaWdodHMgd2hlcmUgYXJyaXZhbCB0aW1lIGlzIGJlZm9yZSBkZXBhcnR1cmUgdGltZQ0KYGBge3J9DQpmbGlnaHRzX2R0ICU+JSANCiAgZmlsdGVyKGFycl90aW1lIDwgZGVwX3RpbWUpIA0KYGBgDQoNClVwZGF0ZSBmbGlnaHRzIGRhdGEgdG8gY29ycmVjdCBvdmVybmlnaHQgZmxpZ2h0cw0KYGBge3J9DQpmbGlnaHRzX2R0IDwtIGZsaWdodHNfZHQgJT4lIA0KICBtdXRhdGUoDQogICAgb3Zlcm5pZ2h0ID0gYXJyX3RpbWUgPCBkZXBfdGltZSwNCiAgICBhcnJfdGltZSA9IGFycl90aW1lICsgZGF5cyhvdmVybmlnaHQgKiAxKSwNCiAgICBzY2hlZF9hcnJfdGltZSA9IHNjaGVkX2Fycl90aW1lICsgZGF5cyhvdmVybmlnaHQgKiAxKQ0KICApDQpgYGANCg0KRmlsdGVyIGZsaWdodHMgd2hlcmUgb3Zlcm5pZ2h0IGNvbmRpdGlvbiBpcyB0cnVlIGFuZCBhcnJpdmFsIHRpbWUgaXMgYmVmb3JlIGRlcGFydHVyZSB0aW1lDQpgYGB7cn0NCmZsaWdodHNfZHQgJT4lIA0KICBmaWx0ZXIob3Zlcm5pZ2h0LCBhcnJfdGltZSA8IGRlcF90aW1lKSANCmBgYA0KDQpJbnRlcnZhbHMNCkNhbGN1bGF0ZSB0aGUgcmF0aW8gb2Ygb25lIHllYXIgaW4gZGF5cw0KYGBge3J9DQp5ZWFycygxKSAvIGRheXMoMSkNCm5leHRfeWVhciA8LSB0b2RheSgpICsgeWVhcnMoMSkNCih0b2RheSgpICUtLSUgbmV4dF95ZWFyKSAvIGRkYXlzKDEpDQoodG9kYXkoKSAlLS0lIG5leHRfeWVhcikgJS8lIGRheXMoMSkNCmBgYA0KDQpEaXNwbGF5IHRpbWUgem9uZSBpbmZvcm1hdGlvbg0KYGBge3J9DQpTeXMudGltZXpvbmUoKQ0KbGVuZ3RoKE9sc29uTmFtZXMoKSkNCmhlYWQoT2xzb25OYW1lcygpKQ0KYGBgDQpgYGB7cn0NCih4MSA8LSB5bWRfaG1zKCIyMDE1LTA2LTAxIDEyOjAwOjAwIiwgdHogPSAiQW1lcmljYS9OZXdfWW9yayIpKQ0KKHgyIDwtIHltZF9obXMoIjIwMTUtMDYtMDEgMTg6MDA6MDAiLCB0eiA9ICJFdXJvcGUvQ29wZW5oYWdlbiIpKQ0KKHgzIDwtIHltZF9obXMoIjIwMTUtMDYtMDIgMDQ6MDA6MDAiLCB0eiA9ICJQYWNpZmljL0F1Y2tsYW5kIikpDQpgYGANCmBgYHtyfQ0KeDEgLSB4Mg0KeDEgLSB4Mw0KYGBgDQojIFBpcGVzDQoNClRhc2s6IFRvIGltcG9ydCB0aGUgcmVxdWlyZWQgbGlicmFyeQ0KYGBge3J9DQpwYWNrYWdlc190b19pbnN0YWxsIDwtIGMoInRpZHl2ZXJzZSIsICJwcnlyIikNCmZvciAocGFja2FnZV9uYW1lIGluIHBhY2thZ2VzX3RvX2luc3RhbGwpIHsNCiAgaWYgKCFyZXF1aXJlTmFtZXNwYWNlKHBhY2thZ2VfbmFtZSwgcXVpZXRseSA9IFRSVUUpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcyhwYWNrYWdlX25hbWUpDQogIH0NCiAgbGlicmFyeShwYWNrYWdlX25hbWUsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkNCn0NCg0KbGlicmFyeShtYWdyaXR0cikNCmBgYA0KDQpDcmVhdGUgZGlhbW9uZCBkYXRhIGFuZCBjYWxjdWxhdGUgdGhlIG9iamVjdCBzaXplcw0KYGBge3J9DQpkaWFtb25kcyA8LSBnZ3Bsb3QyOjpkaWFtb25kcw0KZGlhbW9uZHMyIDwtIGRpYW1vbmRzICU+JSANCiAgZHBseXI6Om11dGF0ZShwcmljZV9wZXJfY2FyYXQgPSBwcmljZSAvIGNhcmF0KQ0KDQpwcnlyOjpvYmplY3Rfc2l6ZShkaWFtb25kcykNCnByeXI6Om9iamVjdF9zaXplKGRpYW1vbmRzMikNCnByeXI6Om9iamVjdF9zaXplKGRpYW1vbmRzLCBkaWFtb25kczIpDQpgYGANCkZ1bmN0aW9ucw0KTm9ybWFsaXplIHRoZSBjb2x1bW5zIG9mIGEgZGF0YSBmcmFtZQ0KYGBge3J9DQpkZiA8LSB0aWJibGU6OnRpYmJsZSgNCiAgYSA9IHJub3JtKDEwKSwNCiAgYiA9IHJub3JtKDEwKSwNCiAgYyA9IHJub3JtKDEwKSwNCiAgZCA9IHJub3JtKDEwKQ0KKQ0KDQpkZiRhIDwtIChkZiRhIC0gbWluKGRmJGEsIG5hLnJtID0gVFJVRSkpIC8gDQogIChtYXgoZGYkYSwgbmEucm0gPSBUUlVFKSAtIG1pbihkZiRhLCBuYS5ybSA9IFRSVUUpKQ0KZGYkYiA8LSAoZGYkYiAtIG1pbihkZiRiLCBuYS5ybSA9IFRSVUUpKSAvIA0KICAobWF4KGRmJGIsIG5hLnJtID0gVFJVRSkgLSBtaW4oZGYkYSwgbmEucm0gPSBUUlVFKSkNCmRmJGMgPC0gKGRmJGMgLSBtaW4oZGYkYywgbmEucm0gPSBUUlVFKSkgLyANCiAgKG1heChkZiRjLCBuYS5ybSA9IFRSVUUpIC0gbWluKGRmJGMsIG5hLnJtID0gVFJVRSkpDQpkZiRkIDwtIChkZiRkIC0gbWluKGRmJGQsIG5hLnJtID0gVFJVRSkpIC8gDQogIChtYXgoZGYkZCwgbmEucm0gPSBUUlVFKSAtIG1pbihkZiRkLCBuYS5ybSA9IFRSVUUpKQ0KYGBgDQpOb3JtYWxpemUgYSBzaW5nbGUgY29sdW1uIG9mIGEgZGF0YSBmcmFtZQ0KYGBge3J9DQooZGYkYSAtIG1pbihkZiRhLCBuYS5ybSA9IFRSVUUpKSAvDQogIChtYXgoZGYkYSwgbmEucm0gPSBUUlVFKSAtIG1pbihkZiRhLCBuYS5ybSA9IFRSVUUpKQ0KYGBgDQpgYGB7cn0NCnggPC0gZGYkYQ0KKHggLSBtaW4oeCwgbmEucm0gPSBUUlVFKSkgLyAobWF4KHgsIG5hLnJtID0gVFJVRSkgLSBtaW4oeCwgbmEucm0gPSBUUlVFKSkNCmBgYA0KYGBge3J9DQpybmcgPC0gcmFuZ2UoeCwgbmEucm0gPSBUUlVFKQ0KKHggLSBybmdbMV0pIC8gKHJuZ1syXSAtIHJuZ1sxXSkNCmBgYA0KYGBge3J9DQpyZXNjYWxlMDEgPC0gZnVuY3Rpb24oeCkgew0KICBybmcgPC0gcmFuZ2UoeCwgbmEucm0gPSBUUlVFKQ0KICAoeCAtIHJuZ1sxXSkgLyAocm5nWzJdIC0gcm5nWzFdKQ0KfQ0KcmVzY2FsZTAxKGMoMCwgNSwgMTApKQ0KYGBgDQoNClJlc2NhbGUgYSB2ZWN0b3IgdG8gdGhlIHJhbmdlIFswLCAxXQ0KYGBge3J9DQpyZXNjYWxlMDEoYygtMTAsIDAsIDEwKSkNCnJlc2NhbGUwMShjKDEsIDIsIDMsIE5BLCA1KSkNCmBgYA0KDQpSZXNjYWxlIGVhY2ggY29sdW1uIG9mIGEgRGF0YUZyYW1lIHRvIHRoZSByYW5nZSBbMCwgMV0NCmBgYHtyfQ0KZGYkYSA8LSByZXNjYWxlMDEoZGYkYSkNCmRmJGIgPC0gcmVzY2FsZTAxKGRmJGIpDQpkZiRjIDwtIHJlc2NhbGUwMShkZiRjKQ0KZGYkZCA8LSByZXNjYWxlMDEoZGYkZCkNCmBgYA0KYGBge3J9DQp4IDwtIGMoMToxMCwgSW5mKQ0KcmVzY2FsZTAxKHgpDQpgYGANCg0KRGVmaW5lIHRoZSByZXNjYWxlMDEgZnVuY3Rpb24gYW5kIGFwcGx5IGl0DQpgYGB7cn0NCnJlc2NhbGUwMSA8LSBmdW5jdGlvbih4KSB7DQogIHJuZyA8LSByYW5nZSh4LCBuYS5ybSA9IFRSVUUsIGZpbml0ZSA9IFRSVUUpDQogICh4IC0gcm5nWzFdKSAvIChybmdbMl0gLSBybmdbMV0pDQp9DQpyZXNjYWxlMDEoeCkNCmBgYA0KDQpMb2FkIHJlcXVpcmVkIGxpYnJhcmllcyBhbmQgcGFja2FnZXMNCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHB1cnJyKQ0KbGlicmFyeShtYWdyaXR0cikNCg0KIyBpbnN0YWxsLnBhY2thZ2VzKCJwcnlyIikNCmxpYnJhcnkocHJ5cikNCmBgYA0KDQojIyAxOC4yIFBpcGluZyBhbHRlcm5hdGl2ZXMNClRoaXMgaXMgYSBwb3B1bGFyIENoaWxkcmVu4oCZcyBwb2VtIHRoYXQgaXMgYWNjb21wYW5pZWQgYnkgaGFuZCBhY3Rpb25zLldl4oCZbGwgc3RhcnQgYnkgZGVmaW5pbmcgYW4gb2JqZWN0IHRvIHJlcHJlc2VudCBsaXR0bGUgYnVubnkgRm9vIEZvbzoNCg0KYGBge3IgMTguMi0xfQ0KIyBmb29fZm9vIDwtIGxpdHRsZV9idW5ueSgpDQpgYGANCiMjIyAxOC4yLjEgSW50ZXJtZWRpYXRlIHN0ZXBzDQpUaGUgc2ltcGxlc3QgYXBwcm9hY2ggaXMgdG8gc2F2ZSBlYWNoIHN0ZXAgYXMgYSBuZXcgb2JqZWN0Og0KDQpgYGB7ciAxOC4yLjEtMX0NCiMgZm9vX2Zvb18xIDwtIGhvcChmb29fZm9vLHRocm91Z2g9Zm9yZXN0KQ0KIyBmb29fZm9vXzIgPC0gc2Nvb3AoZm9vX2Zvb18xLCB1cCA9IGZpZWxkX21pY2UpDQojIGZvb19mb29fMyA8LSBib3AoZm9vX2Zvb18yLCBvbiA9IGhlYWQpDQpgYGANCg0KQ3JlYXRlIGRpYW1vbmRzIGRhdGFzZXQgYW5kIGNhbGN1bGF0ZSBwcmljZSBwZXIgY2FyYXQNCmBgYHtyIDE4LjIuMS0yfQ0KZGlhbW9uZHMgPC0gZ2dwbG90Mjo6ZGlhbW9uZHMNCmRpYW1vbmRzMiA8LSBkaWFtb25kcyAlPiUgDQogIGRwbHlyOjptdXRhdGUocHJpY2VfcGVyX2NhcmF0PXByaWNlL2NhcmF0KQ0KDQpwcnlyOjpvYmplY3Rfc2l6ZShkaWFtb25kcykNCnByeXI6Om9iamVjdF9zaXplKGRpYW1vbmRzMikNCnByeXI6Om9iamVjdF9zaXplKGRpYW1vbmRzLGRpYW1vbmRzMikNCmBgYA0KDQpJbnRyb2R1Y2UgTkEgdmFsdWUgaW50byBkaWFtb25kcyRjYXJhdCBhbmQgY2hlY2sgb2JqZWN0IHNpemVzDQpgYGB7ciAxOC4yLjEtM30NCmRpYW1vbmRzJGNhcmF0WzFdIDwtIE5BDQpwcnlyOjpvYmplY3Rfc2l6ZShkaWFtb25kcykNCnByeXI6Om9iamVjdF9zaXplKGRpYW1vbmRzMikNCnByeXI6Om9iamVjdF9zaXplKGRpYW1vbmRzLGRpYW1vbmRzMikNCmBgYA0KDQojIyMgMTguMi4yIE92ZXJ3cml0ZSB0aGUgb3JpZ2luYWwNCkluc3RlYWQgb2YgY3JlYXRpbmcgaW50ZXJtZWRpYXRlIG9iamVjdHMgYXQgZWFjaCBzdGVwLCB3ZSBjb3VsZCBvdmVyd3JpdGUgdGhlIG9yaWdpbmFsIG9iamVjdDoNCmBgYHtyIDE4LjIuMi0xfQ0KIyBmb29fZm9vIDwtIGhvcChmb29fZm9vLCB0aHJvdWdoID0gZm9yZXN0KQ0KIyBmb29fZm9vIDwtIHNjb29wKGZvb19mb28sIHVwID0gZmllbGRfbWljZSkNCiMgZm9vX2ZvbyA8LSBib3AoZm9vX2Zvbywgb24gPSBoZWFkKQ0KYGBgDQojIyMgMTguMi4zIEZ1bmN0aW9uIGNvbXBvc2l0aW9uDQpBbm90aGVyIGFwcHJvYWNoIGlzIHRvIGFiYW5kb24gYXNzaWdubWVudCBhbmQganVzdCBzdHJpbmcgdGhlIGZ1bmN0aW9uIGNhbGxzIHRvZ2V0aGVyOg0KYGBge3IgMTguMi4zLTF9DQojIGJvcCgNCiMgICBzY29vcCgNCiMgICAgIGhvcChmb29fZm9vLCB0aHJvdWdoID0gZm9yZXN0KSwNCiMgICAgIHVwID0gZmllbGRfbWljZQ0KIyAgICksIA0KIyAgIG9uID0gaGVhZA0KIyApDQpgYGANCg0KSGVyZSB0aGUgZGlzYWR2YW50YWdlIGlzIHRoYXQgeW91IGhhdmUgdG8gcmVhZCBmcm9tIGluc2lkZS1vdXQsIGZyb20gcmlnaHQtdG8tbGVmdCwgYW5kIHRoYXQgdGhlIGFyZ3VtZW50cyBlbmQgdXAgc3ByZWFkIGZhciBhcGFydCAoZXZvY2F0aXZlbHkgY2FsbGVkIHRoZSBkYWd3b29kIHNhbmR3aGljaCBwcm9ibGVtKS4gSW4gc2hvcnQsIHRoaXMgY29kZSBpcyBoYXJkIGZvciBhIGh1bWFuIHRvIGNvbnN1bWUuDQoNCiMjIyAxOC4yLjQgVXNlIHRoZSBwaXBlDQpGaW5hbGx5LCB3ZSBjYW4gdXNlIHRoZSBwaXBlOg0KYGBge3IgMTguMi40LTF9DQojIGZvb19mb28gJT4lDQojICAgaG9wKHRocm91Z2ggPSBmb3Jlc3QpICU+JQ0KIyAgIHNjb29wKHVwID0gZmllbGRfbWljZSkgJT4lDQojICAgYm9wKG9uID0gaGVhZCkNCmBgYA0KDQpgYGB7ciAxOC4yLjQtMn0NCiMgbXlfcGlwZSA8LSBmdW5jdGlvbiguKSB7DQojICAgLiA8LSBob3AoLiwgdGhyb3VnaCA9IGZvcmVzdCkNCiMgICAuIDwtIHNjb29wKC4sIHVwID0gZmllbGRfbWljZSkNCiMgICBib3AoLiwgb24gPSBoZWFkKQ0KIyB9DQojIG15X3BpcGUoZm9vX2ZvbykNCmBgYA0KVEFTSzogIEZ1bmN0aW9ucyB0aGF0IHVzZSB0aGUgY3VycmVudCBlbnZpcm9ubWVudC4gRm9yIGV4YW1wbGUsIGBhc3NpZ24oKWAgd2lsbCBjcmVhdGUgYSBuZXcgdmFyaWFibGUgd2l0aCB0aGUgZ2l2ZW4gbmFtZSBpbiB0aGUgY3VycmVudCBlbnZpcm9ubWVudDoNCmBgYHtyIDE4LjIuNC0zfQ0KYXNzaWduKCJ4IiwxMCkNCngNCg0KIngiICU+JSBhc3NpZ24oMTAwKQ0KeA0KYGBgDQpBc3NpZ24gdmFsdWUgdG8gIngiIGluIHRoZSBzcGVjaWZpZWQgZW52aXJvbm1lbnQgYW5kIGNoZWNrIGl0cyB2YWx1ZSBhbmQgR2VuZXJhdGUgcmFuZG9tIG51bWJlcnMsIGNyZWF0ZSBhIG1hdHJpeCwgcGxvdCBpdCwgYW5kIGluc3BlY3QgaXRzIHN0cnVjdHVyZQ0KYGBge3IgMTguMi40LTR9DQplbnYgPC0gZW52aXJvbm1lbnQoKQ0KIngiICU+JSBhc3NpZ24oMTAwLGVudmlyPWVudikNCngNCmBgYA0KYGBge3IgMTguNC0xfQ0Kcm5vcm0oMTAwKSAlPiUgDQogIG1hdHJpeChuY29sPTIpICU+JSANCiAgcGxvdCgpICU+JSANCiAgc3RyKCkNCg0Kcm5vcm0oMTAwKSAlPiUgDQogIG1hdHJpeChuY29sPTIpICU+JSANCiAgcGxvdCgpICU+JSANCiAgc3RyKCkNCg0KbmRpc3QgPC0gcm5vcm0oMTAwMDAwKQ0KaGlzdChuZGlzdCkNCmBgYA0KDQpDYWxjdWxhdGUgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gdHdvIHZhcmlhYmxlcyBpbiBtdGNhcnMgZGF0YXNldA0KYGBge3IgMTguNC0yfQ0KbXRjYXJzICUkJQ0KICBjb3IoZGlzcCwgbXBnKQ0KYGBgDQoNCi0gRm9yIGFzc2lnbm1lbnQgbWFncml0dHIgcHJvdmlkZXMgdGhlIGAlPD4lYCBvcGVyYXRvciB3aGljaCBhbGxvd3MgeW91IHRvIHJlcGxhY2UgY29kZSBsaWtlOg0KYGBge3IgMTguNC0zfQ0KbXRjYXJzIDwtIG10Y2FycyAlPiUgDQogIHRyYW5zZm9ybShjeWw9Y3lsKjIpDQpgYGANCg0KDQpgYGB7ciAxOC40LTR9DQptdGNhcnMgJTw+JSB0cmFuc2Zvcm0oY3lsPWN5bCoyKQ0KYGBgDQojIENoYXB0ZXIgMTkgRnVuY3Rpb25zDQojIyAxOS4xIEludHJvZHVjdGlvbg0KDQojIyAxOS4yIFdoZW4gc2hvdWxkIHlvdSB3cml0ZSBhIGZ1bmN0aW9uPw0KYGBge3IgMTkuMi0xfQ0KZGYgPC0gdGliYmxlOjp0aWJibGUoDQogIGEgPSBybm9ybSgxMCksDQogIGIgPSBybm9ybSgxMCksDQogIGMgPSBybm9ybSgxMCksDQogIGQgPSBybm9ybSgxMCkNCikNCmRmDQoNCmRmJGEgPC0gKGRmJGEgLSBtaW4oZGYkYSwgbmEucm0gPSBUUlVFKSkgLyANCiAgKG1heChkZiRhLCBuYS5ybSA9IFRSVUUpIC0gbWluKGRmJGEsIG5hLnJtID0gVFJVRSkpDQpkZiRiIDwtIChkZiRiIC0gbWluKGRmJGIsIG5hLnJtID0gVFJVRSkpIC8gDQogIChtYXgoZGYkYiwgbmEucm0gPSBUUlVFKSAtIG1pbihkZiRhLCBuYS5ybSA9IFRSVUUpKQ0KZGYkYyA8LSAoZGYkYyAtIG1pbihkZiRjLCBuYS5ybSA9IFRSVUUpKSAvIA0KICAobWF4KGRmJGMsIG5hLnJtID0gVFJVRSkgLSBtaW4oZGYkYywgbmEucm0gPSBUUlVFKSkNCmRmJGQgPC0gKGRmJGQgLSBtaW4oZGYkZCwgbmEucm0gPSBUUlVFKSkgLyANCiAgKG1heChkZiRkLCBuYS5ybSA9IFRSVUUpIC0gbWluKGRmJGQsIG5hLnJtID0gVFJVRSkpDQpgYGANCg0KUmVzY2FsZSBhIHNpbmdsZSB2YXJpYWJsZSBpbiBhIGRhdGEgZnJhbWUNCmBgYHtyIDE5LjItMn0NCihkZiRhIC0gbWluKGRmJGEsIG5hLnJtID0gVFJVRSkpIC8NCiAgKG1heChkZiRhLCBuYS5ybSA9IFRSVUUpIC0gbWluKGRmJGEsIG5hLnJtID0gVFJVRSkpDQpgYGANCg0KUmVzY2FsZSBhIHNpbmdsZSB2YXJpYWJsZSB3aXRob3V0IGNyZWF0aW5nIGEgbmV3IG9iamVjdA0KYGBge3J9DQp4IDwtIGRmJGENCih4IC0gbWluKHgsIG5hLnJtID0gVCkpIC8gKG1heCh4LCBuYS5ybSA9IFQpLW1pbih4LCBuYS5ybSA9IFQpKQ0KYGBgDQoNClRhc2s6IFRoZXJlIGlzIHNvbWUgZHVwbGljYXRpb24gaW4gdGhpcyBjb2RlLiBXZeKAmXJlIGNvbXB1dGluZyB0aGUgcmFuZ2Ugb2YgdGhlIGRhdGEgdGhyZWUgdGltZXMsIHNvIGl0IG1ha2VzIHNlbnNlIHRvIGRvIGl0IGluIG9uZSBzdGVwOg0KYGBge3J9DQpybmcgPC0gcmFuZ2UoeCwgbmEucm0gPSBUKQ0KKHgtcm5nWzFdKS8ocm5nWzJdLXJuZ1sxXSkNCmBgYA0KDQpQdWxsaW5nIG91dCBpbnRlcm1lZGlhdGUgY2FsY3VsYXRpb25zIGludG8gbmFtZWQgdmFyaWFibGVzIGlzIGEgZ29vZCBwcmFjdGljZSBiZWNhdXNlIGl0IG1ha2VzIGl0IG1vcmUgY2xlYXIgd2hhdCB0aGUgY29kZSBpcyBkb2luZy4gTm93IHRoYXQgSeKAmXZlIHNpbXBsaWZpZWQgdGhlIGNvZGUsIGFuZCBjaGVja2VkIHRoYXQgaXQgc3RpbGwgd29ya3MsIEkgY2FuIHR1cm4gaXQgaW50byBhIGZ1bmN0aW9uOg0KYGBge3IgMTkuMi01fQ0KcmVzY2FsZTAxIDwtIGZ1bmN0aW9uKHgpew0KICBybmcgPC0gcmFuZ2UoeCwgbmEucm0gPSBUKQ0KICAoeC1ybmdbMV0pLyhybmdbMl0tcm5nWzFdKQ0KfQ0KcmVzY2FsZTAxKGMoMCw1LDEwKSkNCmBgYA0KDQpUZXN0IHRoZSByZXNjYWxlMDEgZnVuY3Rpb24gd2l0aCB2YXJpb3VzIGlucHV0cw0KYGBge3IgMTkuMi02fQ0KcmVzY2FsZTAxKGMoLTEwLDAsMTApKQ0KcmVzY2FsZTAxKGMoMSwyLDMsTkEsNSkpDQpgYGANCg0KV2UgY2FuIHNpbXBsaWZ5IHRoZSBvcmlnaW5hbCBleGFtcGxlIG5vdyB0aGF0IHdlIGhhdmUgYSBmdW5jdGlvbjoNCmBgYHtyIDE5LjItN30NCmRmJGEgPC0gcmVzY2FsZTAxKGRmJGEpDQpkZiRiIDwtIHJlc2NhbGUwMShkZiRiKQ0KZGYkYyA8LSByZXNjYWxlMDEoZGYkYykNCmRmJGQgPC0gcmVzY2FsZTAxKGRmJGQpDQpgYGANCg0KUmVzY2FsZSBhIHZlY3RvciB3aXRoIGluZmluaXRlIHZhbHVlcw0KYGBge3IgMTkuMi04fQ0KeCA8LSBjKDE6MTAsSW5mKQ0KcmVzY2FsZTAxKHgpDQpgYGANCg0KQmVjYXVzZSB3ZeKAmXZlIGV4dHJhY3RlZCB0aGUgY29kZSBpbnRvIGEgZnVuY3Rpb24sIHdlIG9ubHkgbmVlZCB0byBtYWtlIHRoZSBmaXggaW4gb25lIHBsYWNlOg0KYGBge3IgMTkuMi05fQ0KcmVzY2FsZTAxIDwtIGZ1bmN0aW9uKHgpew0KICBybmcgPC0gcmFuZ2UoeCxuYS5ybT1ULGZpbml0ZT1UKQ0KICAoeC1ybmdbMV0pLyhybmdbMl0tcm5nWzFdKQ0KfQ0KcmVzY2FsZTAxKHgpDQpgYGANCg0KIyMgMTkuNCBDb25kaXRpb25hbCBleGVjdXRpb24NCkFuIGBpZmAgc3RhdGVtZW50IGFsbG93cyB5b3UgdG8gY29uZGl0aW9uYWxseSBleGVjdXRlIGNvZGUuIA0KSXQgbG9va3MgbGlrZSB0aGlzOg0KYGBge3J9DQojIGlmIChjb25kaXRpb24pIHsNCiAgIyBjb2RlIGV4ZWN1dGVkIHdoZW4gY29uZGl0aW9uIGlzIFRSVUUNCiMgfSBlbHNlIHsNCiAgIyBjb2RlIGV4ZWN1dGVkIHdoZW4gY29uZGl0aW9uIGlzIEZBTFNFDQojIH0NCmBgYA0KDQpEZWZpbmUgYSBmdW5jdGlvbiB0byBjaGVjayBpZiBhbiBvYmplY3QgaGFzIG5hbWVzDQpgYGB7cn0NCmhhc19uYW1lIDwtIGZ1bmN0aW9uKHgpew0KICBubXMgPC0gbmFtZXMoeCkNCiAgaWYoaXMubnVsbChubXMpKXsNCiAgICByZXAoRkFMU0UsbGVuZ3RoKHgpKQ0KICB9ZWxzZSB7DQogICAgIWlzLm5hKG5tcykgJiBubXMgIT0iIg0KICB9DQp9DQpgYGANCiMjIyAxOS40LjEgQ29uZGl0aW9ucw0KaG93IGlmIGNvbmRpdGlvbiB3b3JrcyB3aXRoIHdhcm5pbmdzDQpgYGB7ciAxOS40LjEtMX0NCiMgaWYgKGMoVFJVRSxGQUxTRSkpe30NCiM+IFdhcm5pbmcgaW4gaWYgKGMoVFJVRSwgRkFMU0UpKSB7OiB0aGUgY29uZGl0aW9uIGhhcyBsZW5ndGggPiAxIGFuZCBvbmx5IHRoZQ0KIz4gZmlyc3QgZWxlbWVudCB3aWxsIGJlIHVzZWQNCiM+IE5VTEwNCg0KIyBpZiAoTkEpIHt9DQpgYGANCkNoZWNrIGlmIHR3byBvYmplY3RzIGFyZSBpZGVudGljYWwNCmBgYHtyfQ0KaWRlbnRpY2FsKDBMLDApDQp4IDwtIHNxcnQoMileMg0KeD09Mg0KeC0yDQpgYGANCg0KIyMjIDE5LjQuMiBNdWx0aXBsZSBjb25kaXRpb25zDQpZb3UgY2FuIGNoYWluIG11bHRpcGxlIGlmIHN0YXRlbWVudCB0b2dldGhlcjoNCmBgYHtyIDE5LjQuMi0xfQ0KIyBpZiAodGhpcykgew0KIyAgICMgZG8gdGhhdA0KIyB9IGVsc2UgaWYgKHRoYXQpIHsNCiMgICAjIGRvIHNvbWV0aGluZyBlbHNlDQojIH0gZWxzZSB7DQojICAgIyANCiMgfQ0KYGBgDQoNCmBgYHtyIDE5LjQuMi0yfQ0KIz4gZnVuY3Rpb24oeCwgeSwgb3ApIHsNCiM+ICAgc3dpdGNoKG9wLA0KIz4gICAgIHBsdXMgPSB4ICsgeSwNCiM+ICAgICBtaW51cyA9IHggLSB5LA0KIz4gICAgIHRpbWVzID0geCAqIHksDQojPiAgICAgZGl2aWRlID0geCAvIHksDQojPiAgICAgc3RvcCgiVW5rbm93biBvcCEiKQ0KIz4gICApDQojPiB9DQpgYGANCiMjIyAxOS40LjMgQ29kZSBzdHlsZQ0KDQpHb29kIHByYWN0aWNlIGZvciB3cml0aW5nIGlmIHN0YXRlbWVudHMNCmBgYHtyIDE5LjQuMy0xfQ0KIyBHb29kDQojIGlmICh5IDwgMCAmJiBkZWJ1Zykgew0KIyAgIG1lc3NhZ2UoIlkgaXMgbmVnYXRpdmUiKQ0KIyB9DQojIA0KIyBpZiAoeSA9PSAwKSB7DQojICAgbG9nKHgpDQojIH0gZWxzZSB7DQojICAgeSBeIHgNCiMgfQ0KIyANCiMgIyBCYWQNCiMgaWYgKHkgPCAwICYmIGRlYnVnKQ0KIyBtZXNzYWdlKCJZIGlzIG5lZ2F0aXZlIikNCiMgDQojIGlmICh5ID09IDApIHsNCiMgICBsb2coeCkNCiMgfSANCiMgZWxzZSB7DQojICAgeSBeIHgNCiMgfQ0KYGBgDQoNCkl04oCZcyBvayB0byBkcm9wIHRoZSBjdXJseSBicmFjZXMgaWYgeW91IGhhdmUgYSB2ZXJ5IHNob3J0IGlmIHN0YXRlbWVudCB0aGF0IGNhbiBmaXQgb24gb25lIGxpbmU6IA0KYGBge3IgMTkuNC4zLTJ9DQp5IDwtIDEwDQp4IDwtIGlmICh5IDwgMjApICJUb28gbG93IiBlbHNlICJUb28gaGlnaCINCmBgYA0KDQpJIHJlY29tbWVuZCB0aGlzIG9ubHkgZm9yIHZlcnkgYnJpZWYgYGlmYCBzdGF0ZW1lbnRzLiBPdGhlcndpc2UsIHRoZSBmdWxsIGZvcm0gaXMgZWFzaWVyIHRvIHJlYWQ6DQpgYGB7cn0NCmlmICh5IDwgMjApIHsNCiAgeCA8LSAiVG9vIGxvdyIgDQp9IGVsc2Ugew0KICB4IDwtICJUb28gaGlnaCINCn0NCmBgYA0KDQojIyAxOS41IEZ1bmN0aW9uIGFyZ3VtZW50cyANCg0KYGBge3J9DQojIENvbXB1dGUgY29uZmlkZW5jZSBpbnRlcnZhbCBhcm91bmQgbWVhbiB1c2luZyBub3JtYWwgYXBwcm94aW1hdGlvbg0KbWVhbl9jaSA8LSBmdW5jdGlvbih4LCBjb25mID0gMC45NSkgew0KICBzZSA8LSBzZCh4KSAvIHNxcnQobGVuZ3RoKHgpKQ0KICBhbHBoYSA8LSAxIC0gY29uZg0KICBtZWFuKHgpICsgc2UgKiBxbm9ybShjKGFscGhhIC8gMiwgMSAtIGFscGhhIC8gMikpDQp9DQoNCnggPC0gcnVuaWYoMTAwKQ0KbWVhbl9jaSh4KQ0KDQptZWFuX2NpKHgsIGNvbmYgPSAwLjk5KQ0KDQpgYGANCg0KDQojIyMgMTkuNS4xIENob29zaW5nIG5hbWVzDQojIyMgMTkuNS4yIENoZWtpbmcgdmFsdWVzDQpgYGB7cn0NCnd0X21lYW4gPC0gZnVuY3Rpb24oeCwgdykgew0KICBzdW0oeCAqIHcpIC8gc3VtKHcpDQp9DQp3dF92YXIgPC0gZnVuY3Rpb24oeCwgdykgew0KICBtdSA8LSB3dF9tZWFuKHgsIHcpDQogIHN1bSh3ICogKHggLSBtdSkgXiAyKSAvIHN1bSh3KQ0KfQ0Kd3Rfc2QgPC0gZnVuY3Rpb24oeCwgdykgew0KICBzcXJ0KHd0X3Zhcih4LCB3KSkNCn0NCmBgYA0KDQpXaGF0IGhhcHBlbnMgaWYgeCBhbmQgdyBhcmUgbm90IHRoZSBzYW1lIGxlbmd0aD8NCmBgYHtyfQ0Kd3RfbWVhbigxOjYsIDE6MykNCg0KYGBgDQoNCkluIHRoaXMgY2FzZSwgYmVjYXVzZSBvZiBS4oCZcyB2ZWN0b3IgcmVjeWNsaW5nIHJ1bGVzLCB3ZSBkb27igJl0IGdldCBhbiBlcnJvci4NCg0KSXTigJlzIGdvb2QgcHJhY3RpY2UgdG8gY2hlY2sgaW1wb3J0YW50IHByZWNvbmRpdGlvbnMsIGFuZCB0aHJvdyBhbiBlcnJvciAod2l0aCBgc3RvcCgpYCksIGlmIHRoZXkgYXJlIG5vdCB0cnVlOg0KYGBge3J9DQp3dF9tZWFuIDwtIGZ1bmN0aW9uKHgsIHcpIHsNCiAgaWYgKGxlbmd0aCh4KSAhPSBsZW5ndGgodykpIHsNCiAgICBzdG9wKCJgeGAgYW5kIGB3YCBtdXN0IGJlIHRoZSBzYW1lIGxlbmd0aCIsIGNhbGwuID0gRkFMU0UpDQogIH0NCiAgc3VtKHcgKiB4KSAvIHN1bSh3KQ0KfQ0KYGBgDQoNCmBgYHtyfQ0Kd3RfbWVhbiA8LSBmdW5jdGlvbih4LCB3LCBuYS5ybSA9IEZBTFNFKSB7DQogIGlmICghaXMubG9naWNhbChuYS5ybSkpIHsNCiAgICBzdG9wKCJgbmEucm1gIG11c3QgYmUgbG9naWNhbCIpDQogIH0NCiAgaWYgKGxlbmd0aChuYS5ybSkgIT0gMSkgew0KICAgIHN0b3AoImBuYS5ybWAgbXVzdCBiZSBsZW5ndGggMSIpDQogIH0NCiAgaWYgKGxlbmd0aCh4KSAhPSBsZW5ndGgodykpIHsNCiAgICBzdG9wKCJgeGAgYW5kIGB3YCBtdXN0IGJlIHRoZSBzYW1lIGxlbmd0aCIsIGNhbGwuID0gRkFMU0UpDQogIH0NCiAgDQogIGlmIChuYS5ybSkgew0KICAgIG1pc3MgPC0gaXMubmEoeCkgfCBpcy5uYSh3KQ0KICAgIHggPC0geFshbWlzc10NCiAgICB3IDwtIHdbIW1pc3NdDQogIH0NCiAgc3VtKHcgKiB4KSAvIHN1bSh3KQ0KfQ0KYGBgDQoNClRoaXMgaXMgYSBsb3Qgb2YgZXh0cmEgd29yayBmb3IgbGl0dGxlIGFkZGl0aW9uYWwgZ2Fpbi4gQSB1c2VmdWwgY29tcHJvbWlzZSBpcyB0aGUgYnVpbHQtaW4gYHN0b3BpZm5vdCgpYDogaXQgY2hlY2tzIHRoYXQgZWFjaCBhcmd1bWVudCBpcyBgVFJVRWAsIGFuZCBwcm9kdWNlcyBhIGdlbmVyaWMgZXJyb3IgbWVzc2FnZSBpZiBub3QuDQpgYGB7cn0NCnd0X21lYW4gPC0gZnVuY3Rpb24oeCwgdywgbmEucm0gPSBGQUxTRSkgew0KICBzdG9waWZub3QoaXMubG9naWNhbChuYS5ybSksIGxlbmd0aChuYS5ybSkgPT0gMSkNCiAgc3RvcGlmbm90KGxlbmd0aCh4KSA9PSBsZW5ndGgodykpDQogIA0KICBpZiAobmEucm0pIHsNCiAgICBtaXNzIDwtIGlzLm5hKHgpIHwgaXMubmEodykNCiAgICB4IDwtIHhbIW1pc3NdDQogICAgdyA8LSB3WyFtaXNzXQ0KICB9DQogIHN1bSh3ICogeCkgLyBzdW0odykNCn0NCmBgYA0KIyMjIDE5LjUuMyBEb3QtZG90LWRvdCguLi4pDQpNYW55IGZ1bmN0aW9ucyBpbiBSIHRha2UgYW4gYXJiaXRyYXJ5IG51bWJlciBvZiBpbnB1dHM6IA0KYGBge3IgMTkuNS4zLTF9DQpzdW0oMSwgMiwgMywgNCwgNSwgNiwgNywgOCwgOSwgMTApDQpzdHJpbmdyOjpzdHJfYygiYSIsICJiIiwgImMiLCAiZCIsICJlIiwgImYiKQ0KYGBgDQoNCkRlZmluZSBhIGZ1bmN0aW9uIHRvIGNvbmNhdGVuYXRlIHN0cmluZ3Mgd2l0aCBjb21tYXMNCmBgYHtyIDE5LjUuMy0yfQ0KY29tbWFzIDwtIGZ1bmN0aW9uKC4uLikgc3RyaW5ncjo6c3RyX2MoLi4uLCBjb2xsYXBzZSA9ICIsICIpDQpjb21tYXMobGV0dGVyc1sxOjEwXSkNCg0KDQpydWxlIDwtIGZ1bmN0aW9uKC4uLiwgcGFkID0gIi0iKSB7DQogIHRpdGxlIDwtIHBhc3RlMCguLi4pDQogIHdpZHRoIDwtIGdldE9wdGlvbigid2lkdGgiKSAtIG5jaGFyKHRpdGxlKSAtIDUNCiAgY2F0KHRpdGxlLCAiICIsIHN0cmluZ3I6OnN0cl9kdXAocGFkLCB3aWR0aCksICJcbiIsIHNlcCA9ICIiKQ0KfQ0KcnVsZSgiSW1wb3J0YW50IG91dHB1dCIpDQpgYGANCmBgYHtyfQ0KeCA8LSBjKDEsMikNCnN1bSh4LG5hLnJtPVQpDQpgYGANCg0KRGVmaW5lIGEgZnVuY3Rpb24gJ2NvbXBsaWNhdGVkX2Z1bmN0aW9uJyB3aXRoIGNvbmRpdGlvbnMgdG8gcmV0dXJuIDAgaWYgJ3gnIG9yICd5JyBpcyBlbXB0eQ0KYGBge3J9DQpjb21wbGljYXRlZF9mdW5jdGlvbiA8LSBmdW5jdGlvbih4LHkseil7DQogIGlmIChsZW50aCh4KT09MCB8fCBsZW5ndGgoeSk9PTApew0KICAgIHJldHVybigwKQ0KICB9DQp9DQpgYGANCg0KSW1wcm92ZSByZWFkYWJpbGl0eSBvZiBpZi1lbHNlIGJsb2NrcyBieSB1c2luZyBlYXJseSByZXR1cm4gZm9yIHNpbXBsZSBjYXNlcw0KYGBge3J9DQpmIDwtIGZ1bmN0aW9uKCkgew0KICBpZiAoeCkgew0KICAgICMgRG8gDQogICAgIyBzb21ldGhpbmcNCiAgICAjIHRoYXQNCiAgICAjIHRha2VzDQogICAgIyBtYW55DQogICAgIyBsaW5lcw0KICAgICMgdG8NCiAgICAjIGV4cHJlc3MNCiAgfSBlbHNlIHsNCiAgICAjIHJldHVybiBzb21ldGhpbmcgc2hvcnQNCiAgfQ0KfQ0KYGBgDQoNCkJ1dCBpZiB0aGUgZmlyc3QgYmxvY2sgaXMgdmVyeSBsb25nLCBieSB0aGUgdGltZSB5b3UgZ2V0IHRvIHRoZSBlbHNlLCB5b3XigJl2ZSBmb3Jnb3R0ZW4gdGhlIGNvbmRpdGlvbi4gT25lIHdheSB0byByZXdyaXRlIGl0IGlzIHRvIHVzZSBhbiBlYXJseSByZXR1cm4gZm9yIHRoZSBzaW1wbGUgY2FzZToNCmBgYHtyIDE5LjYuMS0zfQ0KZiA8LSBmdW5jdGlvbigpIHsNCiAgaWYgKCF4KSB7DQogICAgcmV0dXJuKHNvbWV0aGluZ19zaG9ydCkNCiAgfQ0KDQogICMgRG8gDQogICMgc29tZXRoaW5nDQogICMgdGhhdA0KICAjIHRha2VzDQogICMgbWFueQ0KICAjIGxpbmVzDQogICMgdG8NCiAgIyBleHByZXNzDQp9DQpgYGANCg0KVGhpcyB0ZW5kcyB0byBtYWtlIHRoZSBjb2RlIGVhc2llciB0byB1bmRlcnN0YW5kLCBiZWNhdXNlIHlvdSBkb24ndCBuZWVkIHF1aXRlIHNvIG11Y2ggY29udGV4dCB0byB1bmRlcnN0YW5kIGl0Lg0KDQojIyMgMTkuNi4yIFdyaXRpbmcgcGlwZWFibGUgZnVuY3Rpb25zDQpEZWZpbmUgYSBmdW5jdGlvbiB0byBzaG93IHRoZSBjb3VudCBvZiBtaXNzaW5nIHZhbHVlcyBpbiBhIGRhdGEgZnJhbWUNCmBgYHtyIDE5LjYuMi0xfQ0Kc2hvd19taXNzaW5nIDwtIGZ1bmN0aW9uKGRmKXsNCiAgbiA8LSBzdW0oaXMubmEoZGYpKQ0KICBjYXQoIk1pc3NpbmcgdmFsdWVzOiIsbiwiXG4iLHNlcD0iIikNCiAgDQogIGludmlzaWJsZShkZikNCn0NCmBgYA0KDQpJZiB3ZSBjYWxsIGl0IGludGVyYXRpdmVseSwgdGhlIGBpbnZpc2libGUoKWAgbWVhbnMgdGhhdCB0aGUgaW5wdXQgYGRmYCBkb2VzIG5vdCBnZXQgcHJpbnRlZCBvdXQ6IA0KYGBge3J9DQpzaG93X21pc3NpbmcobXRjYXJzKQ0KYGBgDQoNCkJ1dCBpdCdzIHN0aWxsIHRoZXJlLCBpdCdzIG5vdCBqdXN0IHByaW50ZWQgYnkgZGVmYXVsdDoNCmBgYHtyfQ0KeCA8LSBzaG93X21pc3NpbmcobXRjYXJzKQ0KY2xhc3MoeCkNCmRpbSh4KQ0KYGBgDQoNCkFuZCB3ZSBjYW4gc3RpbGwgdXNlIGl0IGluIGEgcGlwZToNCmBgYHtyIDE5LjYuMi00fQ0KbGlicmFyeShtYWdyaXR0cikNCmxpYnJhcnkodGlkeXZlcnNlKQ0KDQptdGNhcnMgJT4lIA0KICBzaG93X21pc3NpbmcoKSAlPiUgDQogIG11dGF0ZShtcGc9aWZlbHNlKG1wZzwyMCxOQSxtcGcpKSAlPiUgDQogIHNob3dfbWlzc2luZygpDQpgYGANCg0KIyMgMTkuNyBFbnZpcm9ubWVudA0KRGVmaW5lIGEgZnVuY3Rpb24gJ2YnIHRoYXQgdGFrZXMgYW4gYXJndW1lbnQgJ3gnIGFuZCByZXR1cm5zIHRoZSBzdW0gb2YgJ3gnIGFuZCAneScNCmBgYHtyIDE5LjctMX0NCmYgPC0gZnVuY3Rpb24oeCl7DQogIHgreQ0KfQ0KYGBgDQpEZW1vbnN0cmF0ZSBob3cgY2hhbmdpbmcgdGhlIHZhbHVlIG9mICd5JyBhZmZlY3RzIHRoZSByZXN1bHQgb2YgY2FsbGluZyBmdW5jdGlvbiAnZicNCmBgYHtyIDE5LjctMn0NCnkgPC0gMTAwDQpmKDEwKQ0KeSA8LSAxMDAwDQpmKDEwKQ0KYGBgDQpPdmVybG9hZCB0aGUgJysnIG9wZXJhdG9yIHRvIGJlaGF2ZSBkaWZmZXJlbnRseSBiYXNlZCBvbiBhIHJhbmRvbSBjb25kaXRpb24NCmBgYHtyIDE5LjctM30NCmArYCA8LSBmdW5jdGlvbih4LCB5KSB7DQogIGlmIChydW5pZigxKSA8IDAuMSkgew0KICAgIHN1bSh4LCB5KQ0KICB9IGVsc2Ugew0KICAgIHN1bSh4LCB5KSAqIDEuMQ0KICB9DQp9DQp0YWJsZShyZXBsaWNhdGUoMTAwMCwgMSArIDIpKQ0KIz4gDQojPiAgIDMgMy4zIA0KIz4gMTAwIDkwMA0Kcm0oYCtgKQ0KYGBgDQojIENoYXB0ZXIgMjA6IFZlY3RvcnMgDQoNCiMjIyAyMC4xLjEgUFJlcmVxdWlzaXRlcyANCg0KYGBge3J9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmBgYA0KDQojIyAyMC4yIFZlY3RvciBiYXNpY3MgDQpEZXRlcm1pbmUgdGhlIGRhdGEgdHlwZSBvZiBkaWZmZXJlbnQgdmVjdG9ycw0KYGBge3J9DQp0eXBlb2YobGV0dGVycykNCnR5cGVvZigxOjEwKQ0KYGBgDQoNCkRldGVybWluZSB0aGUgbGVuZ3RoIG9mIGEgbGlzdCBhbmQgZGlzcGxheSBpdHMgY29udGVudHMNCmBgYHtyfQ0KeCA8LSBsaXN0KCJhIiwiYiIsMToxMCkNCmxlbmd0aCh4KQ0KeA0KYGBgDQpEZW1vbnN0cmF0ZSBtb2R1bG8gb3BlcmF0aW9uIGFuZCBjcmVhdGlvbiBvZiBsb2dpY2FsIHZlY3RvcnMNCmBgYHtyfQ0KMToxMCAlJSAzID09MA0KYyhULFQsRixOQSkNCmBgYA0KDQojIyMgMjAuMy4yIE51bWVyaWMNCkludGVnZXIgYW5kIGRvdWJsZSB2ZWN0b3JzIGFyZSBrbm93biBjb2xsZWN0aXZlbHkgYXMgbnVtZXJpYyB2ZWN0b3JzLiBJbiBSLCBudW1iZXJzIGFyZSBkb3VibGVzIGJ5IGRlZmF1bHQuIFRvIG1ha2UgYW4gaW50ZWdlciwgcGxhY2UgYW4gTCBhZnRlciB0aGUgbnVtYmVyOg0KYGBge3IgMjAuMy4yLTF9DQp0eXBlb2YoMSkNCnR5cGVvZigxTCkNCjEuNQ0KYGBgDQpEZW1vbnN0cmF0ZSB0aGUgYmVoYXZpb3Igb2YgZmxvYXRpbmcgcG9pbnQgYXJpdGhtZXRpYw0KYGBge3IgMjAuMy4yLTJ9DQp4IDwtIHNxcnQoMileMg0KeA0KeC0yDQoNCmBgYA0KRGVtb25zdHJhdGUgdGhlIGJlaGF2aW9yIG9mIGRpdmlzaW9uIGJ5IHplcm8NCmBgYHtyIDIwLjMuMi0zfQ0KYygtMSwwLDEpJS8lIDANCiMgWzFdIC1JbmYgIE5hTiAgSW5mDQpgYGANCg0KDQoNCiMjIyAyMC4zLjMgQ2hhcmFjdGVyDQpEZXRlcm1pbmUgdGhlIG1lbW9yeSBzaXplIG9mIGEgc3RyaW5nIGFuZCBhIHJlcGxpY2F0ZWQgc3RyaW5nIHZlY3Rvcg0KYGBge3IgMjAuMy4zfQ0KeCA8LSAiVGhpcyBpcyBhIHJlYXNvbmFibHkgbG9uZyBzdHJpbmcuIg0KcHJ5cjo6b2JqZWN0X3NpemUoeCkNCg0KeSA8LSByZXAoeCwxMDAwKQ0KcHJ5cjo6b2JqZWN0X3NpemUoeSkNCmBgYA0KDQoNCiMjIyAyMC4zLjQgTWlzc2luZyB2YWx1ZXMNCk5vdGUgdGhhdCBlYWNoIHR5cGUgb2YgYXRvbWljIHZlY3RvciBoYXMgaXRzIG93biBtaXNzaW5nIHZhbHVlOg0KYGBge3J9DQpOQSAgICAgICAgICAgICMgbG9naWNhbA0KDQpOQV9pbnRlZ2VyXyAgICMgaW50ZWdlcg0KDQpOQV9yZWFsXyAgICAgICMgZG91YmxlDQoNCk5BX2NoYXJhY3Rlcl8gIyBjaGFyYWN0ZXINCg0KYGBgDQpDYWxjdWxhdGUgdGhlIG51bWJlciBhbmQgcHJvcG9ydGlvbiBvZiBlbGVtZW50cyBpbiBhIHZlY3RvciBncmVhdGVyIHRoYW4gMTANCmBgYHtyIDIwLjQuMS0xfQ0KeCA8LSBzYW1wbGUoMjAsMTAwLHJlcGxhY2U9VCkNCnkgPC0geCA+IDEwDQpzdW0oeSkgIyBob3cgbWFueSBhcmUgZ3JlYXRlciB0aGFuIDEwPw0KbWVhbih5KSAjIHdoYXQgcHJvcG9ydGlvbiBhcmUgZ3JlYXRlciB0aGFuIDEwPw0KYGBgDQpgYGB7ciAyMC40LjEtMn0NCmlmIChsZW5ndGgoeCkpew0KfQ0KYGBgDQpEZXRlcm1pbmUgdGhlIGRhdGEgdHlwZSBvZiBkaWZmZXJlbnQgdmVjdG9ycw0KYGBge3IgMjAuNC4xLTN9DQp0eXBlb2YoYyhUUlVFLDFMKSkNCnR5cGVvZihjKDFMLDEuNSkpDQp0eXBlb2YoYygxLjUsImEiKSkNCmBgYA0KR2VuZXJhdGUgcmFuZG9tIG51bWVyaWMgb3IgbG9naWNhbCB2ZWN0b3JzDQpgYGB7cn0NCnNhbXBsZSgxMCkrMTAwDQpydW5pZigxMCk+MC41DQpgYGANCkRlbW9uc3RyYXRlIHZlY3RvciBhcml0aG1ldGljIHdpdGggdmVjdG9ycyBvZiBkaWZmZXJlbnQgbGVuZ3Rocw0KYGBge3J9DQoxOjEwICsxOjINCmBgYA0KDQpgYGB7cn0NCjE6MTArMTozDQpgYGANCkNyZWF0ZSBhIHRpYmJsZSB3aXRoIHR3byBjb2x1bW5zLCAneCcgYW5kICd5Jywgd2l0aCBkaWZmZXJlbnQgbGVuZ3Rocw0KYGBge3J9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCg0KDQoNCnRpYmJsZSgNCiAgeD0xOjQsDQogIHk9cmVwKDE6MixlYWNoPTIpDQopDQpgYGANCg0KIyMjIyAyMC40LjQgTmFtaW5nIHZlY3RvcnMNCkFsbCB0eXBlcyBvZiB2ZWN0b3JzIGNhbiBiZSBuYW1lZC4gWW91IGNhbiBuYW1lIHRoZW0gZHVyaW5nIGNyZWF0aW4gd2l0aCBgYygpYDoNCmBgYHtyfQ0KYyh4PTEseT0yLHo9NCkNCmBgYA0KDQpPciBhZnRlciB0aGUgZmFjdCB3aXRoIGBwdXJyOjpzZXRfbmFtZXMoKWANCmBgYHtyfQ0Kc2V0X25hbWVzKDE6MyxjKCJhIiwiYiIsImMiKSkNCmBgYA0KDQpOYW1lZCB2ZWN0b3JzIGFyZSBtb3N0IHVzZWZ1bCBmb3Igc3Vic2V0dGluZywgZGVzY3JpYmVkIG5leHQuDQoNCiMjIyAyMC40LjUgU3Vic2V0dGluZw0KRGVtb25zdHJhdGUgc3Vic2V0dGluZyB2ZWN0b3JzIHdpdGggaW50ZWdlciBpbmRpY2VzDQpgYGB7cn0NCnggPC0gYygib25lIiwidHdvIiwidGhyZWUiLCJmb3VyIiwiZml2ZSIpDQp4W2MoMywyLDUpXQ0KYGBgDQoNCkJ5IHJlcGVhdGluZyBhIHBvc2l0aW9uLCB5b3UgY2FuIGFjdHVhbGx5IG1ha2UgYSBsb25nZXIgb3V0cHV0IHRoYW4gaW5wdXQ6DQpgYGB7cn0NCnhbYygxLDEsNSw1LDUsMildDQpgYGANCg0KTmVnYXRpdmUgdmFsdWVzIGRyb3AgdGhlIGVsZW1lbnRzIGF0IHRoZSBzcGVjaWZpZWQgcG9zaXRpb25zOg0KYGBge3J9DQp4W2MoLTEsLTMsLTUpXQ0KYGBgDQpUaGUgZXJyb3IgbWVzc2FnZSBtZW50aW9ucyBzdWJzZXR0aW5nIHdpdGggemVybywgd2hpY2ggcmV0dXJucyBubyB2YWx1ZXM6DQpgYGB7cn0NCnhbMF0NCmBgYA0KYGBge3J9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCnggPC0gYygxMCwzLE5BLDUsOCwxKQ0KDQojIHRpYmJsZSB0ZXN0DQp4IDwtIGFzLnRpYmJsZSh4LG5jb2w9MSkNCm5hbWVzKHgpPSJ2MSINCmlzLm5hKHgpDQp4ICU+JSBmaWx0ZXIodjEgPT0gTkEpDQoNCiMgYWxsIG5vbi1taXNzaW5nIHZhbHVlcyBvZiB4DQp4IDwtIGMoMTAsMyxOQSw1LDgsMSkNCnhbIWlzLm5hKHgpXQ0KDQojIGFsbCBldmVuIChvciBtaXNzaW5nKSB2YWx1ZXMgb2YgeA0KeFt4ICUlIDI9PTBdDQpgYGANCg0KMy4gSWYgeW91IGhhdmUgYSBuYW1lZCB2ZWN0b3IsIHlvdSBjYW4gc3Vic2V0IGl0IHdpdGggYSBjaGFyYWN0ZXIgdmVjdG9yOg0KYGBge3J9DQp4IDwtIGMoYWJjPTEsIGRlZj0yLHh5ej01KQ0KeFtjKCJ4eXoiLCJkZWYiKV0NCmBgYA0KDQojIyAyMC41IFJlY3Vyc2l2ZSB2ZWN0b3JzIChsaXN0cykNCkNyZWF0ZSBhIGxpc3Qgd2l0aCBudW1lcmljIGVsZW1lbnRzDQpgYGB7ciAyMC41LTF9DQp4IDwtIGxpc3QoMSwyLDMpDQp4DQpgYGANCkRpc3BsYXkgdGhlIHN0cnVjdHVyZSBvZiBsaXN0cyB3aXRoIGFuZCB3aXRob3V0IG5hbWVzDQpgYGB7ciAyMC41LTJ9DQpzdHIoeCkNCnhfbmFtZWQgPC0gbGlzdChhPTEsYj0yLGM9MykNCnN0cih4X25hbWVkKQ0KYGBgDQoNClVubGlrZSBhdG9taWMgdmVjdG9ycywgYGxpc3QoKWAgY2FuIGNvbnRhaW4gYSBtaXggb2Ygb2JqZWN0czoNCmBgYHtyIDIwLjUtM30NCnkgPC0gbGlzdCgiYSIsMUwsMS41LFQpDQpzdHIoeSkNCmBgYA0KDQpMaXN0IGNhbiBldmVuIGNvbnRhaW4gb3RoZXIgbGlzdHMhDQpgYGB7cn0NCnogPC0gbGlzdChsaXN0KDEsMiksbGlzdCgzLDQpKQ0Kc3RyKHopDQpgYGANCg0KIyMjIDIwLjUuMSBWaXN1YWxpemluZyBsaXN0cyANCmBgYHtyIDIwLjUuMX0NCngxIDwtIGxpc3QoYygxLDIpLGMoMyw0KSkNCngyIDwtIGxpc3QobGlzdCgxLDIpLGxpc3QoMyw0KSkNCngzIDwtIGxpc3QoMSxsaXN0KDIsbGlzdCgzKSkpDQp4MQ0KeDINCngzDQpgYGANCiMjIyAyMC41LjIgU3Vic2V0dGluZw0KQ3JlYXRlIGEgbGlzdCAnYScgd2l0aCBuYW1lZCBlbGVtZW50cyBhbmQgZGVtb25zdHJhdGUgc3Vic2V0dGluZw0KYGBge3J9DQphIDwtIGxpc3QoYSA9IDE6MywgYiA9ICJhIHN0cmluZyIsIGMgPSBwaSwgZCA9IGxpc3QoLTEsIC01KSkNCmBgYA0KDQpgYGB7ciAyMC41LjItMn0NCnN0cihhKQ0Kc3RyKGFbMToyXSkNCnN0cihhWzRdKQ0KYGBgDQpEZW1vbnN0cmF0ZSBzdWJzZXR0aW5nIGxpc3RzIHVzaW5nIGRvdWJsZSBzcXVhcmUgYnJhY2tldHMNCmBgYHtyIDIwLjUuMi0zfQ0Kc3RyKGFbWzFdXSkNCnN0cihhW1s0XV0pDQpgYGANCkFjY2VzcyBsaXN0IGVsZW1lbnRzIGJ5IG5hbWUgdXNpbmcgJCBvciBbWyBdXQ0KYGBge3J9DQphJGENCmFbWyJhIl1dDQpgYGANCiMjIDIwLjYgQXR0cmlidXRlcw0KRGVtb25zdHJhdGUgc2V0dGluZyBhbmQgcmV0cmlldmluZyBhdHRyaWJ1dGVzIG9mIHZlY3RvcnMNCmBgYHtyIDIwLjYtMX0NCnggPC0gMToxMA0KYXR0cih4LCJncmVldGluZyIpDQoNCmF0dHIoeCwiZ3JlZXRpbmciKSA8LSAiSGkhIg0KYXR0cih4LCJmYXJld2VsbCIpIDwtICJCeWUhIg0KYXR0cmlidXRlcyh4KQ0KYGBgDQpEZW1vbnN0cmF0ZSBtZXRob2RzIGZvciBjbGFzcyAnRGF0ZScNCmBgYHtyfQ0KYXMuRGF0ZQ0KYGBgDQpgYGB7ciAyMC42LTN9DQptZXRob2RzKCJhcy5EYXRlIikNCmBgYA0KUmV0cmlldmUgc3BlY2lmaWMgbWV0aG9kcyBmb3IgJ2FzLkRhdGUnDQpgYGB7cn0NCmdldFMzbWV0aG9kKCJhcy5EYXRlIiwiZGVmYXVsdCIpDQpnZXRTM21ldGhvZCgiYXMuRGF0ZSIsIm51bWVyaWMiKQ0KYGBgDQojIyMgMjAuNy4xIEZhY3RvcnMgDQpEZW1vbnN0cmF0ZSBjcmVhdGluZyBhIGZhY3RvciBhbmQgaW5zcGVjdGluZyBpdHMgYXR0cmlidXRlcw0KYGBge3J9DQp4IDwtIGZhY3RvcihjKCJhYiIsImNkIiwiYWIiKSxsZXZlbHM9YygiYWIiLCJjZCIsImVkIikpDQp0eXBlb2YoeCkNCmF0dHJpYnV0ZXMoeCkNCmBgYA0KDQojIyMgMjAuNy4yIERhdGVzIGFuZCBkYXRlLXRpbWVzDQpEYXRlcyBpbiBSIGFyZSBudW1lcmljIHZlY3RvcnMgdGhhdCByZXByZXNlbnQgdGhlIG51bWJlciBvZiBkYXlzIHNpbmNlIDEgSmFudWFyeSAxOTcwLg0KYGBge3IgMjAuNy4yLTF9DQp4IDwtIGFzLkRhdGUoIjE5NzEtMDEtMDEiKQ0KdW5jbGFzcyh4KQ0KDQp0eXBlb2YoeCkNCmF0dHJpYnV0ZXMoeCkNCmBgYA0KRGVtb25zdHJhdGUgY3JlYXRpbmcgYW5kIGluc3BlY3RpbmcgYSBkYXRlLXRpbWUgb2JqZWN0DQpgYGB7ciAyMC43LjItMn0NCnggPC0gbHVicmlkYXRlOjp5bWRfaG0oIjE5NzAtMDEtMDEgMDE6MDAiKQ0KdW5jbGFzcyh4KQ0KDQp0eXBlb2YoeCkNCmF0dHJpYnV0ZXMoeCkNCmBgYA0KRGVtb25zdHJhdGUgc2V0dGluZyBhbmQgcmV0cmlldmluZyB0aW1lIHpvbmUgZm9yIGRhdGUtdGltZSBvYmplY3QNCmBgYHtyIDIwLjcuMi0zfQ0KYXR0cih4LCJ0em9uZSIpIDwtICJVUy9QYWNpZmljIg0KeA0KDQphdHRyKHgsInR6b25lIikgPC0gIlVTL0Vhc3Rlcm4iDQp4DQpgYGANCg0KVGhlcmUgaXMgYW5vdGhlciB0eXBlIG9mIGRhdGUtdGltZXMgY2FsbGVkIFBPU0lYSXQuIFRoZXJlIGFyZSBidWlsdCBvbiB0b3Agb2YgbmFtZWQgbGlzdHM6DQpgYGB7cn0NCnkgPC0gYXMuUE9TSVhsdCh4KQ0KdHlwZW9mKHkpDQojPiBbMV0gImxpc3QiDQphdHRyaWJ1dGVzKHkpDQpgYGANCg0KIyMjIDIwLjcuMyBUaWJibGVzDQpUaWJibGVzIGFyZSBhdWdtZW50ZWQgbGlzdHM6IHRoZXkgaGF2ZSBjbGFzcyDigJx0YmxfZGbigJ0gKyDigJx0YmzigJ0gKyDigJxkYXRhLmZyYW1l4oCdLCBhbmQgYG5hbWVzYCAoY29sdW1uKSBhbmQgYHJvdy5uYW1lc2AgYXR0cmlidXRlczoNCmBgYHtyIDIwLjcuMy0xfQ0KdGIgPC0gdGliYmxlOjp0aWJibGUoeCA9IDE6NSwgeSA9IDU6MSkNCnR5cGVvZih0YikNCmF0dHJpYnV0ZXModGIpDQoNCmBgYA0KYGBge3IgMjAuNy4zLTJ9DQpkZiA8LSBkYXRhLmZyYW1lKHggPSAxOjUsIHkgPSA1OjEpDQp0eXBlb2YoZGYpDQphdHRyaWJ1dGVzKGRmKQ0KYGBgDQojIENoYXB0ZXIgMjE6IEl0ZXJhdGlvbg0KIyMjIDIxLjEuMSBQcmVyZXF1aXNpdGVzDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KYGBgDQoNCiMjIDIxLjIgRm9yIGxvb3BzDQpJbWFnaW5lIHdlIGhhdmUgdGhpcyBzaW1wbGUgdGliYmxlOg0KYGBge3J9DQpkZiA8LSB0aWJibGUoDQogIGE9cm5vcm0oMTApLA0KICBiPXJub3JtKDEwKSwNCiAgYz1ybm9ybSgxMCksDQogIGQ9cm5vcm0oMTApDQopDQpgYGANCkNhbGN1bGF0ZSB0aGUgbWVkaWFuIGZvciBlYWNoIGNvbHVtbiBpbiBhIHRpYmJsZSANCmBgYHtyfQ0KbWVkaWFuKGRmJGEpDQptZWRpYW4oZGYkYikNCm1lZGlhbihkZiRjKQ0KbWVkaWFuKGRmJGQpDQpgYGANCkNhbGN1bGF0ZSB0aGUgbWVkaWFuIGZvciBlYWNoIGNvbHVtbiBpbiB0aGUgZGF0YSBmcmFtZSAnZGYnIHVzaW5nIGEgZm9yIGxvb3ANCmBgYHtyfQ0KZGYNCm91dHB1dCA8LSB2ZWN0b3IoImRvdWJsZSIsbmNvbChkZikpDQpmb3IgKGkgaW4gc2VxX2Fsb25nKGRmKSl7DQogIG91dHB1dFtbaV1dIDwtIG1lZGlhbihkZltbaV1dKQ0KfQ0Kb3V0cHV0IDwtIHRpYmJsZShvdXRwdXQpDQpgYGANCkRlbW9uc3RyYXRlIHRoZSBiZWhhdmlvciBvZiBzZXFfYWxvbmcgYW5kIGxlbmd0aCBmdW5jdGlvbnMgd2l0aCBhbiBlbXB0eSB2ZWN0b3IgJ3knDQpgYGB7cn0NCnkgPC0gdmVjdG9yKCJkb3VibGUiLCAwKQ0Kc2VxX2Fsb25nKHkpDQojPiBpbnRlZ2VyKDApDQoxOmxlbmd0aCh5KQ0KIz4gWzFdIDEgMA0KYGBgDQojIyMgMjEuMy4xdiBNb2RpZnlpbmcgYW4gZXhpc3Rpbmcgb2JqZWN0DQpTb21ldGltZXMsIHlvdSB3YW50IHRvIHVzZSBhIGZvciBsb29wIHRvIG1vZGlmeSBhbiBleGlzdGluZyBvYmplY3QuIEZvciBleGFtcGxlLCByZW1lbWJlciBvdXIgY2hhbGxlbmdlcyBmcm9tIGZ1bmN0aW9ucy4gV2Ugd2FudGVkIHRvIHJlc2NhbGUgZXZlcnkgY29sdW1uIGluIGEgZGF0YSBmcmFtZToNCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQoNCmRmIDwtIHRpYmJsZSgNCiAgYT1ybm9ybSgxMCksDQogIGI9cm5vcm0oMTApLA0KICBjPXJub3JtKDEwKSwNCiAgZD1ybm9ybSgxMCkNCikNCg0KcmVzY2FsZTAxIDwtIGZ1bmN0aW9uKHgpew0KICBybmcgPC0gcmFuZ2UoeCxuYS5ybT1UKQ0KICAoeC1ybmdbMV0pLyhybmdbMl0tcm5nWzFdKQ0KfQ0KDQpkZiRhIDwtIHJlc2NhbGUwMShkZiRhKQ0KZGYkYiA8LSByZXNjYWxlMDEoZGYkYikNCmRmJGMgPC0gcmVzY2FsZTAxKGRmJGMpDQpkZiRkIDwtIHJlc2NhbGUwMShkZiRkKQ0KDQpkZg0KYGBgDQoNCmBgYHtyfQ0KZm9yICggaSBpbiBzZXFfYWxvbmcoZGYpKXsNCiAgZGZbW2ldXSA8LSByZXNjYWxlMDEoZGZbW2ldXSkNCn0NCmBgYA0KDQojIyMgMjEuMy4yIExvb3BpbmcgcGF0dGVybnMNCmBgYHtyfQ0KeA0KcmVzdWx0cyA8LSB2ZWN0b3IoImxpc3QiLGxlbmd0aCh4KSkNCm5hbWVzKHJlc3VsdHMpIDwtIG5hbWVzKHgpDQpgYGANCg0KRGVtb25zdHJhdGUgbG9vcGluZyBwYXR0ZXJucyB1c2luZyBhIGZvciBsb29wIHRvIGl0ZXJhdGUgb3ZlciBhIGxpc3QgJ3gnIGFuZCBzdG9yZSByZXN1bHRzIGluIGEgbGlzdCAncmVzdWx0cycNCmBgYHtyfQ0KZm9yKGkgaW4gc2VxX2Fsb25nKHgpKXsNCiAgbmFtZSA8LSBuYW1lcyh4KVtbaV1dDQogIHZhbHVlIDwtIHhbW2ldXQ0KfQ0KYGBgDQoNCiMjIyAyMS4zLjMgVW5rbm93biBvdXRwdXQgbGVuZ3RoDQpDcmVhdGUgYSB2ZWN0b3IgJ291dHB1dCcgd2l0aCB1bmtub3duIGxlbmd0aCBhbmQgc3RvcmUgcmVzdWx0cyBmcm9tIGEgZm9yIGxvb3AgaW4gaXQNCmBgYHtyfQ0KbWVhbnMgPC0gYygwLDEsMikNCg0Kb3V0cHV0IDwtIGRvdWJsZSgpDQpmb3IgKGkgaW4gc2VxX2Fsb25nKG1lYW5zKSl7DQogIG4gPC0gc2FtcGxlKDEwMCwxKQ0KICBvdXRwdXQgPC0gYyhvdXRwdXQscm5vcm0obixtZWFuc1tbaV1dKSkNCn0NCnN0cihvdXRwdXQpDQpvdXRwdXQNCmBgYA0KDQpDcmVhdGUgYSBsaXN0ICdvdXQnIHdpdGggdW5rbm93biBsZW5ndGggYW5kIHN0b3JlIHJlc3VsdHMgZnJvbSBhIGZvciBsb29wIGluIGl0DQpgYGB7cn0NCm91dCA8LSB2ZWN0b3IoImxpc3QiLGxlbmd0aChtZWFucykpDQpmb3IgKGkgaW4gc2VxX2Fsb25nKG1lYW5zKSl7DQogIG4gPC0gc2FtcGxlKDEwMCwxKQ0KICBvdXRbW2ldXSA8LSBybm9ybShuLG1lYW5zW1tpXV0pDQp9DQpzdHIob3V0KQ0Kc3RyKHVubGlzdChvdXQpKQ0KYGBgDQojIyMgMjEuMy40IFVua25vd24gc2VxdWVuY2UgbGVuZ3RoDQoNCg0KQSB3aGlsZSBsb29wIGlzIGFsc28gbW9yZSBnZW5lcmFsIHRoYW4gYSBmb3IgbG9vcCwgYmVjYXVzZSB5b3UgY2FuIHJld3JpdGUgYW55IGZvciBsb29wIGFzIGEgd2hpbGUgbG9vcCwgYnV0IHlvdSBjYW4ndCByZXdyaXRlIGV2ZXJ5IHdoaWxlIGxvb3AgYXMgZm9yIGxvb3A6DQpgYGB7cn0NCmZvciAoaSBpbiBzZXFfYWxvbmcoeCkpIHsNCiAgIyBib2R5DQp9DQoNCiMgRXF1aXZhbGVudCB0bw0KaSA8LSAxDQp3aGlsZSAoaSA8PSBsZW5ndGgoeCkpIHsNCiAgIyBib2R5DQogIGkgPC0gaSArIDEgDQp9DQoNCmBgYA0KDQpIZXJob3cgd2UgY291bGQgdXNlIGEgd2hpbGUgbG9vcCB0byBmaW5kIGhvdyBtYW55IHRyaWVzIGl0IHRha2VzIHRvIGdldCB0aHJlZSBoZWFkcyBpbiBhIHJvdzogDQpgYGB7cn0NCmZsaXAgPC0gZnVuY3Rpb24oKSBzYW1wbGUoYygiVCIsICJIIiksIDEpDQoNCmZsaXBzIDwtIDANCm5oZWFkcyA8LSAwDQoNCndoaWxlIChuaGVhZHMgPCAzKSB7DQogIGlmIChmbGlwKCkgPT0gIkgiKSB7DQogICAgbmhlYWRzIDwtIG5oZWFkcyArIDENCiAgfSBlbHNlIHsNCiAgICBuaGVhZHMgPC0gMA0KICB9DQogIGZsaXBzIDwtIGZsaXBzICsgMQ0KfQ0KZmxpcHMNCmBgYA0KIyMgMjEuNCBGb3IgbG9vcHMgdnMuIGZ1bmN0aW9uYWxzDQpDb21wYXJlIGZvciBsb29wIGFuZCBmdW5jdGlvbmFsIGFwcHJvYWNoZXMgZm9yIGNhbGN1bGF0aW5nIGNvbHVtbiBtZWFucyBpbiBhIGRhdGEgZnJhbWUNCmBgYHtyfQ0KZGYgPC0gdGliYmxlKA0KICBhPXJub3JtKDEwKSwNCiAgYj1ybm9ybSgxMCksDQogIGM9cm5vcm0oMTApLA0KICBkPXJub3JtKDEwKQ0KKQ0KYGBgDQpVc2luZyBmb3IgbG9vcA0KYGBge3J9DQpvdXRwdXQgPC0gdmVjdG9yKCJkb3VibGUiLGxlbmd0aChkZikpDQpmb3IgKGkgaW4gc2VxX2Fsb25nKGRmKSl7DQogIG91dHB1dFtbaV1dIDwtIG1lYW4oZGZbW2ldXSkNCn0NCm91dHB1dA0KYGBgDQpVc2luZyBmdW5jdGlvbmFsIGFwcHJvYWNoIHdpdGggYSBjdXN0b20gZnVuY3Rpb24gJ2NvbF9tZWFuJw0KYGBge3J9DQpjb2xfbWVhbiA8LSBmdW5jdGlvbihkZil7DQogIG91dHB1dCA8LSB2ZWN0b3IoImRvdWJsZSIsbGVuZ3RoKGRmKSkNCiAgZm9yIChpIGluIHNlcV9hbG9uZyhkZikpew0KICAgIG91dHB1dFtpXSA8LSBtZWFuKGRmW1tpXV0pDQogIH0NCiAgb3V0cHV0DQp9DQpgYGANCkRlZmluZSBhIGZ1bmN0aW9uICdjb2xfbWVkaWFuJyB0byBjYWxjdWxhdGUgdGhlIG1lZGlhbiBmb3IgZWFjaCBjb2x1bW4gaW4gdGhlIGRhdGEgZnJhbWUgJ2RmJw0KYGBge3J9DQpjb2xfbWVkaWFuIDwtIGZ1bmN0aW9uKGRmKXsNCiAgb3V0cHV0IDwtIHZlY3RvcigiZG91YmxlIixoaChkZikpDQogIGZvciAoaSBpbiBzZXFfYWxvbmcoZGYpKXsNCiAgICBvdXRwdXRbaV0gPC0gbWVkaWFuKGRmW1tpXV0pDQogIH0NCiAgb3V0cHV0DQp9DQoNCmNvbF9zZCA8LSBmdW5jdGlvbihkZil7DQogIG91dHB1dCA8LSB2ZWN0b3IoImRvdWJsZSIsbGVuZ3RoKGRmKSkNCiAgZm9yIChpIGluIHNlcV9hbG9uZyhkZikpew0KICAgIG91dHB1dFtpXSA8LSBzZChkZltbaV1dKQ0KICB9DQogIG91dHB1dA0KfQ0KDQpkZg0KYGBgDQpEZWZpbmUgZnVuY3Rpb25zIGYxLCBmMiwgYW5kIGYzIGZvciBjYWxjdWxhdGluZyBkaWZmZXJlbnQgcG93ZXJzIG9mIGFic29sdXRlIGRldmlhdGlvbiBmcm9tIHRoZSBtZWFuDQpgYGB7cn0NCmYxIDwtIGZ1bmN0aW9uKHgpIGFicyh4LW1lYW4oeCkpXjENCmYyIDwtIGZ1bmN0aW9uKHgpIGFicyh4LW1lYW4oeCkpXjINCmYzIDwtIGZ1bmN0aW9uKHgpIGFicyh4LW1lYW4oeCkpXjMNCmBgYA0KRGVmaW5lIGEgZnVuY3Rpb24gJ2YnIHRvIGNhbGN1bGF0ZSB0aGUgYWJzb2x1dGUgZGV2aWF0aW9uIGZyb20gdGhlIG1lYW4gcmFpc2VkIHRvIGEgZ2l2ZW4gcG93ZXIgJ2knDQpgYGB7cn0NCmYgPC0gZnVuY3Rpb24oeCxpKSBhYnMoeC1tZWFuKHgpKV5pDQpgYGANCkRlZmluZSBhIGZ1bmN0aW9uICdjb2xfc3VtbWFyeScgdG8gYXBwbHkgYSBzdW1tYXJ5IGZ1bmN0aW9uICdmdW4nIHRvIGVhY2ggY29sdW1uIG9mIHRoZSBkYXRhIGZyYW1lICdkZicNCmBgYHtyfQ0KY29sX3N1bW1hcnkgPC0gZnVuY3Rpb24oZGYsIGZ1bikgew0KICBvdXQgPC0gdmVjdG9yKCJkb3VibGUiLCBsZW5ndGgoZGYpKQ0KICBmb3IgKGkgaW4gc2VxX2Fsb25nKGRmKSkgew0KICAgIG91dFtpXSA8LSBmdW4oZGZbW2ldXSkNCiAgfQ0KICBvdXQNCn0NCmNvbF9zdW1tYXJ5KGRmLCBtZWRpYW4pDQpjb2xfc3VtbWFyeShkZiwgbWVhbikNCmBgYA0KRGVtb25zdHJhdGUgdGhlIHVzZSBvZiAnbWFwX2RibCcgZnJvbSB0aGUgJ3B1cnJyJyBwYWNrYWdlIHRvIGFwcGx5IGEgZnVuY3Rpb24gdG8gZWFjaCBjb2x1bW4gb2YgdGhlIGRhdGEgZnJhbWUgJ2RmJw0KYGBge3J9DQpsaWJyYXJ5KHB1cnJyKQ0KaGVhZChkZikNCg0KDQojIFJlZmVyZW5jZSAtIGZvciBsb29wKCkNCm91dHB1dCA8LSB2ZWN0b3IoImRvdWJsZSIsbGVuZ3RoKGRmKSkNCmZvciAoaSBpbiBzZXFfYWxvbmcoZGYpKXsNCiAgb3V0cHV0W1tpXV0gPC0gbWVhbihkZltbaV1dKQ0KfQ0Kb3V0cHV0DQoNCm1hcF9kYmwoZGYsbWVhbikNCm1hcF9kYmwoZGYsbWVkaWFuKQ0KbWFwX2RibChkZixzZCkNCmBgYA0KDQpgYGB7cn0NCmRmICU+JSBtYXBfZGJsKG1lYW4pDQpkZiAlPiUgbWFwX2RibChtZWRpYW4pDQpkZiAlPiUgbWFwX2RibChzZCkNCmBgYA0KRGVtb25zdHJhdGUgdGhlIHVzZSBvZiAnbWFwX2RibCcgZnJvbSB0aGUgJ3B1cnJyJyBwYWNrYWdlIHdpdGggYWRkaXRpb25hbCBhcmd1bWVudHMNCmBgYHtyfQ0KbWFwX2RibChkZixtZWFuLHRyaW09MC41KQ0KYGBgDQpEZW1vbnN0cmF0ZSB0aGUgdXNlIG9mICdtYXBfaW50JyBmcm9tIHRoZSAncHVycnInIHBhY2thZ2UgdG8gYXBwbHkgYSBmdW5jdGlvbiB0aGF0IHJldHVybnMgaW50ZWdlcnMgdG8gZWFjaCBlbGVtZW50IG9mIGEgbGlzdA0KYGBge3J9DQp6IDwtIGxpc3QoeD0xOjMseT00OjUpDQp6DQoNCm1hcF9pbnQoeixsZW5ndGgpDQpgYGANCg0KIyMjIDIxLjUuMSBTaG9ydGN1dHMgDQpEZW1vbnN0cmF0ZSB0aGUgdXNlIG9mICdzYWZlbHknIGZyb20gdGhlICdwdXJycicgcGFja2FnZSB0byBjcmVhdGUgYSBzYWZlIHZlcnNpb24gb2YgYSBmdW5jdGlvbg0KDQpgYGB7cn0NCnNhZmVfbG9nIDwtIHNhZmVseShsb2cpDQpzdHIoc2FmZV9sb2coMTApKQ0Kc3RyKHNhZmVfbG9nKCJhIikpDQpgYGANCkRlbW9uc3RyYXRlIHRoZSB1c2Ugb2YgJ21hcCcgZnJvbSB0aGUgJ3B1cnJyJyBwYWNrYWdlIHdpdGggJ3NhZmVseScgdG8gYXBwbHkgYSBzYWZlIHZlcnNpb24gb2YgYSBmdW5jdGlvbiB0byBlYWNoIGVsZW1lbnQgb2YgYSBsaXN0DQoNCmBgYHtyfQ0KeCA8LSBsaXN0KDEsMTAsImEiKQ0KeSA8LSB4ICU+JSBtYXAoc2FmZWx5KGxvZykpDQpzdHIoeSkNCg0KYGBgDQpEZW1vbnN0cmF0ZSB0aGUgdXNlIG9mICd0cmFuc3Bvc2UnIGZyb20gdGhlICdwdXJycicgcGFja2FnZSB0byB0cmFuc3Bvc2UgYSBsaXN0IG9mIGxpc3RzDQpgYGB7cn0NCnkgPC0geCAlPiUgdHJhbnNwb3NlKCkNCnN0cih5KQ0KYGBgDQpEZW1vbnN0cmF0ZSB0aGUgdXNlIG9mIGVycm9yIGhhbmRsaW5nIHdpdGggJ21hcF9sZ2wnIGFuZCAnaXNfbnVsbCcgZnJvbSB0aGUgJ3B1cnJyJyBwYWNrYWdlDQpgYGB7cn0NCmlzX29rIDwtIHkkZXJyb3IgJT4lIG1hcF9sZ2woaXNfbnVsbCkNCnhbIWlzX29rXQ0KIyB5JHJlc3VsdFtpc19va10gJT4lIGZsYXR0ZW5fZGJsKCkNCmBgYA0KDQpQdXJyciBwcm92aWRlcyB0d28gb3RoZXIgdXNlZnVsIGFkdmVyYnM6DQpgYGB7cn0NCnggPC0gbGlzdCgxLDEwLCJhIikNCnggJT4lIG1hcF9kYmwocG9zc2libHkobG9nLE5BX3JlYWxfKSkNCmBgYA0KRGVtb25zdHJhdGUgdGhlIHVzZSBvZiAncXVpZXRseScgZnJvbSB0aGUgJ3B1cnJyJyBwYWNrYWdlIHRvIHN1cHByZXNzIGVycm9ycyBhbmQgcmV0dXJuIHJlc3VsdHMgd2l0aCB3YXJuaW5ncw0KYGBge3J9DQp4IDwtIGxpc3QoMSwtMSkNCnggJT4lIG1hcChxdWlldGx5KGxvZykpICU+JSBzdHIoKQ0KYGBgDQoNCg0KIyMgMjEuNyBNYXBwaW5nIG92ZXIgbXVsdGlwbGUgYXJndW1lbnRzIA0KR2VuZXJhdGUgcmFuZG9tIG51bWJlcnMgZnJvbSBub3JtYWwgZGlzdHJpYnV0aW9ucyB3aXRoIGRpZmZlcmVudCBtZWFucyB1c2luZyAnbWFwJyBmcm9tIHRoZSAncHVycnInIHBhY2thZ2UNCmBgYHtyfQ0KbXUgPC0gbGlzdCg1LDEwLC0zKQ0KbXUgJT4lIA0KICBtYXAocm5vcm0sbj01KSAlPiUgDQogIHN0cigpDQpgYGANCkdlbmVyYXRlIHJhbmRvbSBudW1iZXJzIGZyb20gbm9ybWFsIGRpc3RyaWJ1dGlvbnMgd2l0aCBkaWZmZXJlbnQgbWVhbnMgYW5kIHN0YW5kYXJkIGRldmlhdGlvbnMgdXNpbmcgJ21hcDInIGZyb20gdGhlICdwdXJycicgcGFja2FnZQ0KYGBge3J9DQpzaWdtYSA8LSBsaXN0KDEsNSwxMCkNCnNlcV9hbG9uZyhtdSkgJT4lIA0KICBtYXAofnJub3JtKDUsbXVbWy5dXSxzaWdtYVtbLl1dKSkgJT4lIA0KICBzdHIoKQ0KYGBgDQpEZWZpbmUgYSBjdXN0b20gJ21hcDInIGZ1bmN0aW9uIHRvIGFwcGx5IGEgYmluYXJ5IGZ1bmN0aW9uIHRvIGNvcnJlc3BvbmRpbmcgZWxlbWVudHMgb2YgdHdvIGxpc3RzDQpgYGB7cn0NCm1hcDIobXUsc2lnbWEscm5vcm0sbj01KSAlPiUgc3RyKCkNCmBgYA0KDQpgYGB7cn0NCm1hcDIgPC0gZnVuY3Rpb24oeCx5LGYsLi4uKXsNCiAgb3V0IDwtIHZlY3RvcigibGlzdCIsbGVuZ3RoKHgpKQ0KICBmb3IgKGkgaW4gc2VxX2Fsb25nKHgpKXsNCiAgICBvdXRbW2ldXSA8LSBmKHhbW2ldXSx5W1tpXV0sLi4uKQ0KICB9DQogIG91dA0KfQ0KYGBgDQpBcHBseSBhIGZ1bmN0aW9uIHRvIGNvcnJlc3BvbmRpbmcgZWxlbWVudHMgb2YgbXVsdGlwbGUgbGlzdHMgdXNpbmcgJ3BtYXAnIGZyb20gdGhlICdwdXJycicgcGFja2FnZQ0KYGBge3J9DQoNCmxpYnJhcnkobWFncml0dHIpDQpsaWJyYXJ5KHB1cnJyKQ0KDQpuIDwtIGxpc3QoMSwzLDUpDQphcmdzMSA8LSBsaXN0KG4sbXUsc2lnbWEpDQphcmdzMSAlPiUgDQogIHBtYXAocm5vcm0pICU+JSANCiAgc3RyKCkNCmBgYA0KQXBwbHkgYSBmdW5jdGlvbiB0byBjb3JyZXNwb25kaW5nIGVsZW1lbnRzIG9mIG11bHRpcGxlIGxpc3RzIHdpdGggbmFtZWQgcGFyYW1ldGVycyB1c2luZyAncG1hcCcgZnJvbSB0aGUgJ3B1cnJyJyBwYWNrYWdlDQpgYGB7cn0NCmFyZ3MyIDwtIGxpc3QobWVhbj1tdSwgc2Q9c2lnbWEsbj1uKQ0KYXJnczIgJT4lIA0KICBwbWFwKHJub3JtKSAlPiUgDQogIHN0cigpDQpgYGANCkFwcGx5IGEgZnVuY3Rpb24gdG8gY29ycmVzcG9uZGluZyByb3dzIG9mIGEgZGF0YSBmcmFtZSB1c2luZyAncG1hcCcgZnJvbSB0aGUgJ3B1cnJyJyBwYWNrYWdlIHdpdGggYSB0aWJibGUNCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpwYXJtcyA8LSB0cmliYmxlKA0KICB+bWVhbix+c2Qsfm4sDQogIDUsMSwxLA0KICAxMCw1LDMsDQogIC0zLDEwLDUNCikNCg0KcGFybXMgJT4lIA0KICBwbWFwKHJub3JtKQ0KYGBgDQojIyMgMjEuNy4xIEludm9saW5nIGRpZmZlcmVudCBmdW5jdGlvbnMNCkludm9rZSBkaWZmZXJlbnQgZnVuY3Rpb25zIHdpdGggZGlmZmVyZW50IHBhcmFtZXRlcnMgdXNpbmcgJ2ludm9rZV9tYXAnIGZyb20gdGhlICdwdXJycicgcGFja2FnZQ0KYGBge3J9DQpmIDwtIGMoInJ1bmlmIiwicm5vcm0iLCJycG9pcyIpDQpwYXJhbSA8LSBsaXN0KA0KICBsaXN0KG1pbj0tMSxtYXg9MSksDQogIGxpc3Qoc2Q9NSksDQogIGxpc3QobGFtYmRhPTEwKQ0KKQ0KDQpmDQpwYXJhbQ0KYGBgDQoNClRvIGhhbmRsZSB0aGlzIGNhc2UsIHlvdSBjYW4gdXNlIGBpbnZva2VfbWFwKClgOg0KYGBge3J9DQppbnZva2VfbWFwKGYscGFyYW0sbj01KSAlPiUgDQogIHN0cigpDQpgYGANCkludm9rZSBkaWZmZXJlbnQgZnVuY3Rpb25zIHdpdGggZGlmZmVyZW50IHBhcmFtZXRlcnMgdXNpbmcgJ3BtYXAnIGZyb20gdGhlICdwdXJycicgcGFja2FnZSBhbmQgYSB0aWJibGUNCmBgYHtyfQ0Kc2ltIDwtIHRyaWJibGUoDQogIH5mLCAgICAgIH5wYXJhbXMsDQogICJydW5pZiIsIGxpc3QobWluID0gLTEsIG1heCA9IDEpLA0KICAicm5vcm0iLCBsaXN0KHNkID0gNSksDQogICJycG9pcyIsIGxpc3QobGFtYmRhID0gMTApDQopDQpzaW0gJT4lIA0KICBtdXRhdGUoc2ltID0gaW52b2tlX21hcChmLCBwYXJhbXMsIG4gPSAxMCkpDQpgYGANCg0KIyMgMjEuOCBXYWxrDQpQZXJmb3JtIHNpZGUgZWZmZWN0cyB3aXRob3V0IHJldHVybmluZyBhIHZhbHVlIGZvciBlYWNoIGVsZW1lbnQgb2YgYSBsaXN0IHVzaW5nICd3YWxrJyBmcm9tIHRoZSAncHVycnInIHBhY2thZ2UNCmBgYHtyfQ0KeCA8LSBsaXN0KDEsImEiLDMpDQp4ICU+JSANCiAgd2FsayhwcmludCkNCmBgYA0KUGVyZm9ybSBzaWRlIGVmZmVjdHMgb24gZWFjaCBlbGVtZW50IG9mIGEgbGlzdCB1c2luZyAnd2FsaycgZnJvbSB0aGUgJ3B1cnJyJyBwYWNrYWdlLCB0aGVuIHNhdmUgdGhlIHJlc3VsdHMNCmBgYHtyfQ0KbGlicmFyeShnZ3Bsb3QyKQ0KcGxvdHMgPC0gbXRjYXJzICU+JSANCiAgc3BsaXQoLiRjeWwpICU+JSANCiAgbWFwKH5nZ3Bsb3QoLiwgYWVzKG1wZywgd3QpKSArIGdlb21fcG9pbnQoKSkNCnBhdGhzIDwtIHN0cmluZ3I6OnN0cl9jKG5hbWVzKHBsb3RzKSwgIi5wZGYiKQ0KDQpwd2FsayhsaXN0KHBhdGhzLCBwbG90cyksIGdnc2F2ZSwgcGF0aCA9IHRlbXBkaXIoKSkNCmBgYA0KUmV0YWluIG9yIHJlbW92ZSBlbGVtZW50cyBvZiBhIGxpc3QgYmFzZWQgb24gYSBwcmVkaWNhdGUgZnVuY3Rpb24gdXNpbmcgJ2tlZXAnIGFuZCAnZGlzY2FyZCcgZnJvbSB0aGUgJ3B1cnJyJyBwYWNrYWdlDQpgYGB7cn0NCmlyaXMgJT4lIA0KICBrZWVwKGlzLmZhY3RvcikgJT4lIA0KICBzdHIoKQ0KDQppcmlzICU+JSANCiAgZGlzY2FyZChpcy5mYWN0b3IpICU+JQ0KICBzdHIoKQ0KYGBgDQoNCg0KDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShtYWdyaXR0cikNCg0KDQpgYGANCg0KIyMjIDIxLjkuMiBSZWR1Y2UgYW5kIGFjY3VtdWxhdGUNCkl0ZXJhdGl2ZWx5IGNvbWJpbmUgZWxlbWVudHMgb2YgYSBsaXN0IHVzaW5nIGEgYmluYXJ5IGZ1bmN0aW9uIHdpdGggJ3JlZHVjZScgZnJvbSB0aGUgJ3B1cnJyJyBwYWNrYWdlDQpgYGB7cn0NCmRmcyA8LSBsaXN0KA0KICBhZ2U9dGliYmxlKG5hbWU9IkpvaG4iLGFnZT0zMCksDQogIHNleD10aWJibGUobmFtZT1jKCJKb2huIiwiTWFyeSIpLHNleD1jKCJNIiwiRiIpKSwNCiAgdHJ0PXRpYmJsZShuYW1lPSJNYXJ5Iix0cmVhdG1lbnQ9IkEiKQ0KKQ0KDQpkZnMgJT4lIHJlZHVjZShmdWxsX2pvaW4pDQpgYGANCkZpbmQgdGhlIGludGVyc2VjdGlvbiBvZiBtdWx0aXBsZSB2ZWN0b3JzIHVzaW5nICdyZWR1Y2UnIGZyb20gdGhlICdwdXJycicgcGFja2FnZQ0KYGBge3J9DQp2cyA8LSBsaXN0KA0KICBjKDEsMyw1LDYsMTApLA0KICBjKDEsMiwzLDcsOCwxMCksDQogIGMoMSwyLDMsNCw4LDksMTApDQopDQp2cyAlPiUgcmVkdWNlKGludGVyc2VjdCkNCmBgYA0KSXRlcmF0aXZlbHkgYXBwbHkgYSBmdW5jdGlvbiB0byBlbGVtZW50cyBvZiBhIGxpc3QgdXNpbmcgJ2FjY3VtdWxhdGUnIGZyb20gdGhlICdwdXJycicgcGFja2FnZQ0KYGBge3J9DQp4IDwtIHNhbXBsZSgxMCkNCngNCnggJT4lIGFjY3VtdWxhdGUoYCtgKQ0KYGBgDQoNCg==